为何C传栈字符串给Rust动态库会触发munmap_chunk错误?
问题描述
我用Rust编写了一个对外暴露的C兼容函数listen,它接收*const c_char类型参数并转换为String类型,代码如下:
#[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()) }; } }
在C程序中,直接传入命令行参数argv[1]时,程序会抛出munmap_chunk(): invalid pointer错误;但通过malloc在堆上分配内存复制该参数内容后传入,程序可正常运行。
出错的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; }
正常运行的C代码:
int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage %s <listen_addr>\n", argv[0]); return -1; } const char *addr = argv[1]; int addr_len = strlen(addr) + 1; char *addr_buffer = (char *)malloc(addr_len); strcpy(addr_buffer, addr); listen(addr_buffer); return 0; }
请问为何传递栈内存字符串会触发错误,堆内存字符串可正常工作?
问题原因
核心问题出在String::from_raw_parts的使用逻辑上:
- 这个Rust函数要求传入的指针必须是由Rust内存分配器分配、且可被安全释放的内存地址。当
addr_str离开作用域时,Rust会自动调用自身分配器释放这块内存。 argv[1]指向的内存是操作系统在程序启动时分配的,属于进程的静态/栈内存区域,既不属于Rust分配器管理,也不能被Rust的释放逻辑操作。Rust尝试释放不属于它管辖的内存,直接触发munmap_chunk(): invalid pointer这类内存错误。- 用
malloc分配的堆内存,虽然是C标准库分配的,但大多数场景下Rust默认全局分配器是对C标准库malloc/free的封装,二者内存管理逻辑兼容,因此Rust可以安全释放这块内存,程序就能正常运行。
此外,你的Rust代码还存在隐藏风险:
CStr::to_bytes()返回的是不含末尾\0的字节切片,将其const指针强制转为mut指针的操作不安全——如果原内存是只读的(比如argv[1]通常是只读区域),后续若String尝试修改内容,会触发未定义行为。
正确的实现方式:
通过复制原字符串内容到Rust管理的内存中,彻底规避内存所有权冲突:
#[no_mangle] pub extern "C" fn listen(addr: *const c_char) { unsafe { let cstr = CStr::from_ptr(addr); // 转换为Rust字符串并复制到新内存 let addr_str = cstr.to_str().unwrap_or_default().to_string(); // 后续使用addr_str即可 } }
内容的提问来源于Stack Exchange,提问作者ChrisFisher
相关产品推荐
相关产品推荐

