我有一个函数在Rust. 它接收 a*const c_char并将其转换为字符串。
#[no_mangle]
pub extern "C" fn listen(addr: *const c_char) {
unsafe {
let addr_str = {
let cstr = CStr::from_ptr(addr);
let bytes = cstr.to_bytes();
String::from_raw_parts(bytes.as_ptr() as *mut u8, bytes.len(), bytes.len())
};
}
}
Run Code Online (Sandbox Code Playgroud)
在 C 程序中,我从命令行获取地址。
#include "xxx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// input: address
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage %s <listen_addr>\n", argv[0]);
return -1;
}
const char *addr = argv[1];
listen(addr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它会抛出错误munmap_chunk(): invalid pointer。但是,如果我选择将地址存储在堆上,程序就不会抛出错误。
#include "xxx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
// input: address
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage %s <listen_addr>\n", argv[0]);
return -1;
}
const char *addr = argv[1];
listen(addr);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么把地址放在栈上不行,而放在堆上却可以呢?
为什么把地址放在栈上不行,而放在堆上却可以呢?
因为这:
Run Code Online (Sandbox Code Playgroud)String::from_raw_parts(bytes.as_ptr() as *mut u8, bytes.len(), bytes.len())
在这两种情况下都是完全不健全的:
String是一个拥有的类型,当它超出范围时,它的内容会被释放,但在第一种情况下,因为它几乎肯定不是直接分配的(仅隐式地作为堆栈的一部分——在大多数情况下,尽管实际上并不能保证)分配器当您要求它释放就其而言不是有效指针的内容时,会出现错误(这是最好的情况)。
即使你将字符串移动到“堆”,它仍然不健全,因为不能保证你的 C 和 Rust 运行时使用相同的分配器,你不能用另一个分配器释放一个分配器的分配。
这两个版本也不健全,因为 Rust 要求String的缓冲区为 UTF8,但没有对此进行任何验证。
CStr::to_str考虑到有一种方法可以正确地完成所有正确的事情(因此它甚至不是不安全的),整个事情绝对令人困惑。