Home > OS >  rust library returned Box object is automatically freed in C
rust library returned Box object is automatically freed in C

Time:09-21

I have a feeling that ONE rust Box<Context> (as struct Context * in C) is automatically freed when leaving main.

I have a rust library /home/codes/libspeakdet.so from following code in lib.rs compiled with cargo build

pub struct Context { pub sim: f32 }

#[no_mangle]
pub extern "C" fn make_context() -> Box<Context> {
    Box::new(Context { sim: 0.2733f32 })
}

#[no_mangle]
pub extern "C" fn drop_context(_ctx: Option<Box<Context>>) {}

And test.c compiled with cc -fsanitize=address -fno-omit-frame-pointer -o test -g test.c /home/codes/libspeakdet.so

struct Context *make_context();
void drop_context(struct Context *ctx);

int main(int argc, char *argv[])
{
    struct Context *ctx;

    ctx = make_context();
    //drop_context(ctx);
    ctx = make_context();

    return 0;
}

with the drop_context line commented, running test got output

==44143==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 4 byte(s) in 1 object(s) allocated from:
...

and no leak report with the drop_context line uncommented. Having N make_context calls will have asan report N-1 objects direct leak.

I'm confused, because I think the ownership is a rust feature that frees memory.

How could a C program automatically free that ONE ctx when leaving main?

EDIT I think use Box to receive C pointer is a example from rust document. module level document of Box. Please help me understand it if I'm wrong.

... Here, the struct Foo* type from C is translated to Box, which captures the ownership constraints. ...

the document have EXACT code as mine.

EDIT I added these codes just before return 0 to verify the still reachable explain provided by the answers.

    //ctx = NULL;
    ctx = (struct Context *)malloc(7);
    //free(ctx);
  • setting ctx = NULL has no change to the asan report.
  • setting ctx to malloced 7 bytes made asan report 7 bytes leak.
  • after free the malloced 7 bytes, asan report became same as without these lines.

EDIT MORE

  • parallel experiment

compile the following c code to independent libleaktest.so, and link test.c to libleaktest.so instead, asan reports N leaks.

struct Context *make_context() {
    struct Context *ctx = (struct Context *)malloc(9);
    return ctx;
}

void drop_context(struct Context *ctx) {
    free(ctx);
}

CodePudding user response:

I'm confused, because I think the ownership is a rust feature that frees memory.

Yes. The operative word is Rust feature. C is not Rust, C does not understand Box, C doesn't have drop.

Returning Rust objects by value to C is not correct. You need to use Box::into_raw to convert the Box to a raw pointer before returning it to C, then you need a freeing function which converts back from_raw into a Box which Rust can drop.

The second operation is obviously wildly unsafe, Rust has no way to verify whether C is lying, so C could provide random garbage or provide the same raw pointer twice, both of which lead to UBs.

How could a C program automatically free that ONE ctx when leaving main?

It can't. That's just not part of C's semantics.

CodePudding user response:

How could a C program automatically free that ONE ctx when leaving main?

It can't, and doesn't, but you're misinterpreting the leak sanitizer.

From the design doc (with my emphasis):

The leak checker ... first halts the execution of the process. This ensures that the pointer graph does not change while we examine it, and allows us to collect any transient pointers from register contexts of running threads.

LSan scans this memory for byte patterns that look like pointers to heap blocks ... Those blocks are considered reachable and their contents are also treated as live memory. In this manner we discover all blocks reachable from the root set. We then iterate over all existing heap blocks and report unreachable blocks as leaks

If the leak sanitizer finds the address of your Context in the C variable ctx, the object will be considered reachable and not reported as a leak.


The other way to answer this question

How could a C program automatically free that ONE ctx when leaving main?

is to point out that everything is freed when you leave main. Program exit reclaims all the program resources (apart from perhaps shared memory and similar cases).

Technically it even reclaims unreachable allocations, because the whole process address space is getting torn down. The reason unreachable allocations are considered leaks is that they could accumulate invisibly during process runtime.

  • Related