这个使用Windows API的Rust代码是“安全的”吗?

时间:2018-02-19 20:01:32

标签: winapi rust

[.]

我使用Rust对Windows API的绑定编写了这个函数,我我至少涵盖了use winapi::um::processthreadsapi::OpenProcess; use winapi::shared::minwindef::{DWORD, FALSE}; use winapi::shared::ntdef::NULL; use winapi::um::winnt::{HANDLE, PROCESS_ALL_ACCESS}; use winapi::um::errhandlingapi::GetLastError; pub fn get_process_full_access(process_id: DWORD) -> Result<HANDLE, DWORD> { unsafe { let process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id); if process == NULL { Err(GetLastError()) } else { Ok(process) } } } 函数的文档错误案例,但我仍然不确定我的代码的实际安全性。

我知道Rust提供了与防止竞争条件相关的保证,但我真的不知道该寻找什么以确保我的代码能够提供相同的保证。在这样的情况下,我是否应该使包装功能不安全,或者是否值得在我的程序中防止不安全的冒泡?

2 个答案:

答案 0 :(得分:2)

进程ID存在竞争。要免于竞争,您必须验证pid是否与预期的进程开始时间匹配(重新启动管理器为此使用RM_UNIQUE_PROCESS)。理想情况下,您将保留所创建的子进程的句柄,只要某人有一个打开的进程句柄,就无法重用进程ID。

如果它不是您的子进程并且您没有句柄,则必须在找到进程ID并调用get_process_full_access之前获取系统时间,然后在进程句柄上调用GetProcessTimes并确认创建时间早于您在查找流程ID 并打开流程之前阅读的时间。

答案 1 :(得分:0)

通常,Windows API实现基本的异常保证:函数要么运行完毕,要么返回错误。无论哪种情况,都不会违反不变式。关于OpenProcess API,只有两个结果:

  • 该函数失败(由返回值NULL表示)。
  • 该函数成功执行并返回一个有效的HANDLE,直到在其上调用CloseHandle为止。

get_process_full_access的实现考虑了所有结果,并相应地传播结果。因此,它很安全,不需要标记为unsafe

但是,接口仍然不安全:它返回原始的HANDLE值,该值在被删除时不会释放其引用的资源。这需要包装在一个结构中,该结构至少要实现Drop特质(尽管Clone不会伤害到两者):

pub struct KernelObj {
    handle: HANDLE,
}

impl Drop for KernelObj {
    fn drop(&mut self) {
        unsafe { CloseHandle( self.handle ) };
    }
}

实现需要像这样更新:

pub fn get_process_full_access(process_id: DWORD) -> Result<KernelObj, DWORD> {
    unsafe {
        let process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
        if process == NULL {
            Err(GetLastError())
        } else {
            Ok(KernelObj{ handle: process })
        }
    }
}

clone()必须致电DuplicateHandle。同样,您可以选择实现SyncSend标记特征,因为并发共享访问以及转移到任意线程都是安全的操作。