munmap_chunk:使用 Rust 生成的动态库中的函数的 C 程序中的无效指针

Chr*_*her 0 c ffi rust

我有一个函数在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)

为什么把地址放在栈上不行,而放在堆上却可以呢?

Mas*_*inn 6

为什么把地址放在栈上不行,而放在堆上却可以呢?

因为这:

String::from_raw_parts(bytes.as_ptr() as *mut u8, bytes.len(), bytes.len())
Run Code Online (Sandbox Code Playgroud)

在这两种情况下都是完全不健全的:

  1. String是一个拥有的类型,当它超出范围时,它的内容会被释放,但在第一种情况下,因为它几乎肯定不是直接分配的(仅隐式地作为堆栈的一部分——在大多数情况下,尽管实际上并不能保证)分配器当您要求它释放就其而言不是有效指针的内容时,会出现错误(这是最好的情况)。

  2. 即使你将字符串移动到“堆”,它仍然不健全,因为不能保证你的 C 和 Rust 运行时使用相同的分配器,你不能用另一个分配器释放一个分配器的分配。

这两个版本也不健全,因为 Rust 要求String的缓冲区为 UTF8,但没有对此进行任何验证。

CStr::to_str考虑到有一种方法可以正确地完成所有正确的事情(因此它甚至不是不安全的),整个事情绝对令人困惑。