如何防止 Rust 程序释放()它不拥有的 C 字节

tek*_*aul 2 rust

libc 有一个函数返回一个不应该是 free()d 的字符串。可以读取用户当前目录,/etc/passwd但不应是 free()d。如果我从指针创建一个字符串,程序会立即中止

free(): invalid pointer
Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)

显然,代码是不安全的。

let pw = libc::getpwnam(username.as_ptr() as *const i8);
let cd = (*pw).pw_dir;
let len = libc::strlen(cd);
builder.current_dir(String::from_raw_parts(cd as *mut u8, len, len));
Run Code Online (Sandbox Code Playgroud)

我可以mem::forget(s)使用字符串,但会泄漏内存,至少长度必须存储在某处。我只需要防止释放底层原始字节。项目是一个服务器,所以我不能忽视泄漏。

use*_*342 5

String::from_raw_parts是此处使用的错误函数。该函数只能使用先前从String::into_raw_parts或等效的指针调用,这在您的代码中显然不是这种情况,其中指针来自 C 库并且它指向的内存甚至没有动态分配。当使用标记为不安全的 API 时,例如from_raw_parts,应仔细查看文档,在这种情况下,该文档将函数标记为“高度不安全”,准确地说是调用者必须遵守的约定。

相反,您应该&str使用slice::from_raw_partsstr::from_utf8或使用CStr专为此目的设计的类型来创建指向数据的指针。一旦你有了&str,你应该将它复制到一个拥有的字符串中并builder.current_dir()用它调用。调用builder.current_dir()&str将编译,但将是不安全的,需要你证明该功能(和它调用的函数)调用从来没有getpwnam

下面是一个例子:

let pw = libc::getpwnam(username.as_ptr() as *const i8);
let cd = (*pw).pw_dir;
let cd_string = CStr::from_ptr(cd).to_str().unwrap().to_owned();
builder.current_dir(&cd_string);
Run Code Online (Sandbox Code Playgroud)

请注意,此代码仍然不健全,因为getpwnam它不是线程安全的。您应该getpwnam_r改用,或者更好的是,使用像dirs这样的板条箱为您完成所有工作。

这个问题涉及相同的潜在问题,但目标是创建一个Vec.