[.]
我使用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提供了与防止竞争条件相关的保证,但我真的不知道该寻找什么以确保我的代码能够提供相同的保证。在这样的情况下,我是否应该使包装功能不安全,或者是否值得在我的程序中防止不安全的冒泡?
答案 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。同样,您可以选择实现Sync和Send标记特征,因为并发共享访问以及转移到任意线程都是安全的操作。