Skip to content

Commit 998fb48

Browse files
committed
Clarify safety at and around SHGetKnownFolderPath call
Even though this is in test and not production code, I should've included `SAFETY:` comments. This adds those, marks a funtion `unsafe` whose caller needs to consider safety, and refactors the code so that the added `SAFETY:` comments' significance is always immediately clear.
1 parent 1c7a34e commit 998fb48

File tree

1 file changed

+17
-8
lines changed

1 file changed

+17
-8
lines changed

gix-path/src/env/git/tests.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -368,30 +368,39 @@ mod locations {
368368
);
369369
}
370370

371-
/// Owner of a `PWSTR` that must be freed with `CoTaskMemFree`.
371+
/// Owner of a null-terminated `PWSTR` that must be freed with `CoTaskMemFree`.
372372
struct CoStr {
373373
pwstr: PWSTR,
374374
}
375375

376376
impl CoStr {
377-
fn new(pwstr: PWSTR) -> Self {
377+
/// SAFETY: The caller must ensure `pwstr` is a non-null pointer to the beginning of a
378+
/// null-terminated (zero-codepoint-terminated) wide string releasable by `CoTaskMemFree`.
379+
unsafe fn new(pwstr: PWSTR) -> Self {
378380
Self { pwstr }
379381
}
382+
383+
fn to_os_string(&self) -> OsString {
384+
// SAFETY: We know `pwstr` is derefrenceable and the string is null-terminated.
385+
let wide = unsafe { self.pwstr.as_wide() };
386+
OsString::from_wide(wide)
387+
}
380388
}
381389

382390
impl Drop for CoStr {
383391
fn drop(&mut self) {
392+
// SAFETY: `pwstr` is allowed to be passed to `CoTaskMemFree`. (We happen to know it's
393+
// non-null, but `CoTaskMemFree` permits null as well, so the cast is doubly safe.)
384394
unsafe { CoTaskMemFree(Some(self.pwstr.as_ptr().cast::<c_void>())) };
385395
}
386396
}
387397

388398
fn get_known_folder_path_with_flag(id: GUID, flag: KNOWN_FOLDER_FLAG) -> WindowsResult<PathBuf> {
389-
unsafe {
390-
SHGetKnownFolderPath(&id, flag, None)
391-
.map(CoStr::new)
392-
.map(|costr| OsString::from_wide(costr.pwstr.as_wide()))
393-
.map(PathBuf::from)
394-
}
399+
// SAFETY: `SHGetKnownFolderPath` in the `windows` crate wraps the API function and returns
400+
// a non-null pointer to a null-terminated wide string, or an error, not a null pointer.
401+
// As in the wrapped API function, the pointer it returns can be passed to `CoTaskMemFree`.
402+
let costr = unsafe { CoStr::new(SHGetKnownFolderPath(&id, flag, None)?) };
403+
Ok(PathBuf::from(costr.to_os_string()))
395404
}
396405

397406
#[derive(Clone, Copy, Debug)]

0 commit comments

Comments
 (0)