为什么从 C 调用 Rust 库会导致内存泄漏?

cod*_*ppy 0 c memory memory-leaks ffi rust

我试图证明 C 可以调用 Rust 库。它有效,但是当我监视程序的内存使用情况时,它不断变得越来越大。

货物.toml:

[lib]
name="test_ccallr"
crate-type=["cdylib"]
path = "src/lib.rs"

[dependencies]
libc = "0.2"
Run Code Online (Sandbox Code Playgroud)

C代码:

[lib]
name="test_ccallr"
crate-type=["cdylib"]
path = "src/lib.rs"

[dependencies]
libc = "0.2"
Run Code Online (Sandbox Code Playgroud)

我使用以下命令构建 C 代码:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

struct Payload_t{
    char* data;
    int len;
};

int show_str(char* raw, struct Payload_t *data);

int main()
{
    while(1){
        char * p_raw = "hlease print it !ello this is string from c, please print it !";
        char * p_raw_ = "hhello this is another  ";

        char * pp = (char* )malloc(strlen(p_raw));
        memcpy(pp, p_raw, strlen(p_raw));

        struct Payload_t *tmp = (struct Payload_t*)malloc(sizeof (struct Payload_t));
        struct Payload_t *_tmp = tmp;
        tmp->data = (char*)malloc(strlen(p_raw_));
        memcpy(tmp->data, p_raw_, strlen(p_raw_));
        tmp->len = strlen(p_raw_);
        printf("\n%d   %d   %d", pp, tmp, tmp->data);
        show_str(pp, tmp);
        printf("\n%d   %d   %d", pp, tmp, tmp->data);
        free(pp);
        free(tmp->data);
        //free(_tmp);
        usleep(5);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

铁锈代码:

gcc main.c -L ./ -Bstatic -l:libtest_ccallr.so -o test[lib]
Run Code Online (Sandbox Code Playgroud)

如果我取消注释//free(_tmp);,程序就会崩溃。如果我保留此行注释并运行该程序,它可以运行,但会泄漏内存。

有什么不合适的地方吗?

She*_*ter 5

这是不恰当的用法Box::from_raw

let payload_ptr: Payload_t = unsafe { *Box::from_raw(data as *mut Payload_t) };
Run Code Online (Sandbox Code Playgroud)

从文档中,强调我的(阅读不安全函数的文档):

调用此函数后,原始指针归结果Box. 具体来说,析构函数Box将调用 的析构函数T并释放分配的内存。为了安全起见,必须根据 所使用的内存布局来分配内存Box

Box::new您应该仅对您要重新获得所有权的数据分配的数据使用此函数。你的情况两者都不是。Box正在释放 C 分配的内存。这很可能是导致您调用的原因free崩溃的原因,因为 Rust 用它自己的析构函数在内存上乱写乱画。

编写 Rust 函数的更好方法不会尝试获取 的所有权data

#[no_mangle]
pub unsafe extern "C" fn show_str(raw: *const c_char, data: *const Payload_t) -> i32 {
    let c_str = CStr::from_ptr(raw);
    let str_from_ptr = match c_str.to_str() {
        Ok(s) => s,
        Err(err) => {
            eprintln!("{}", err);
            return 0;
        }
    };

    let payload_ptr = &*data;
    let payload = std::slice::from_raw_parts(payload_ptr.data, payload_ptr.len as usize);
    let s = match std::str::from_utf8(payload) {
        Ok(s) => s,
        Err(err) => {
            eprintln!("{}", err);
            return 0;
        }
    };

    println!("the raw string is {}", str_from_ptr);
    println!("the length string is {}", s);

    0
}
Run Code Online (Sandbox Code Playgroud)