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);
}
EDIT MORE
some extra experiment result
valgrind is reporting N
blocks of definitely lost
as expected.
When Context
size is <=
460 bytes, the last leak is not reported in asan.
When Context
size is >=
461 bytes, the last leak is reported in asan.
Declare ctx
as ctx[1]
, and use ctx[0]
instead of ctx
in following code, the last leak is reported in asan (with 4B size Context).
CONCLUSION
Thank you for help. I'm pretty sure now, that the N-1
report behavior is asan bug on rust code. If that block is reachable with 460B size, then it cannot become unreachable with 461B size.
P.S. Fortunately, after commenting each log4rs
and println
line, valgrind is quiet and correct again. That was initial reason why I came to use asan instead.
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.
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.