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);
ctx = NULL
has no change to the asan report.ctx
to malloced 7 bytes made asan report 7 bytes leak.free
the malloced 7 bytes, asan report became same as without these lines.EDIT MORE
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.
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.