• Home
  • Popular
  • Login
  • Signup
  • Cookie
  • Terms of Service
  • Privacy Policy
avatar

Posted by User Bot


25 Apr, 2025

Updated at 20 May, 2025

Why does allocating memory with Box and releasing via FFI not leak memory

I have a rust library which performs some task and returns data, allocated on the heap. The library is used via FFI, but I have an unexplained issue: why does this not result in a memory leak? Produce should allocate data on the heap in each run, but valgrind says everything was free'd. The lib is compiled with cargo rustc --crate-type=staticlib, then the c++ file is linked to the produced .a file. The header file was generated using cbindgen.

NOTE I am aware that Box::from_raw is not the appropriate way, Using into_raw and then freeing with from_raw is the appropriate way to share a Box with ffi.

Rust lib:

use std::slice::from_raw_parts_mut;

#[repr(C)]
#[derive(Debug, Clone)]
pub enum OuterEnum {
    Blank,
    One(Box),
    Two(SomethingElse),
}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct SomethingElse {
    pub one: u32,
}

#[repr(C)]
#[derive(Debug, Clone)]
pub enum InnerEnum {
    One,
    Two(Box),
}

#[repr(C)]
#[derive(Debug, Clone, Default)]
pub struct SomeOtherThing {
    pub one: usize,
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn test() -> Box {
    Box::new(SomeOtherThing::default())
}

#[unsafe(no_mangle)]
pub unsafe extern "C" fn produce(msgs: *mut OuterEnum, size: usize) {
    let data = vec![
        OuterEnum::One(Box::new(InnerEnum::Two(Box::new(SomeOtherThing {
            one: 2,
        })))),
        OuterEnum::Two(SomethingElse { one: 3 }),
    ];
    let msgs = unsafe { from_raw_parts_mut(msgs, size) };
    msgs[..size].clone_from_slice(&data[..size]);
}

impl OuterEnum {
    #[unsafe(no_mangle)]
    pub unsafe extern "C" fn free_outer_enum(&mut self) {
        match *self {
            Self::Blank => unreachable!(),
            OuterEnum::One(ref mut s) => {
                drop(unsafe { Box::from_raw(s.as_mut()) });
            }
            OuterEnum::Two(_) => {}
        }
    }
}

C++ calling code

#include 
#include "header.h"

int main() {
    OuterEnum* msgs = new OuterEnum[1];
    msgs[0].tag = OuterEnum_Tag::OuterEnum_Blank;
    for (int i =0; i < 10; i++) {
        produce(msgs, 1);
    }
    free_outer_enum(msgs);
    delete msgs;
    return 0;
}

Valgrind output:

==29288== Memcheck, a memory error detector
==29288== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==29288== Using Valgrind-3.22.0-bd4db67b1d-20231031 and LibVEX; rerun with -h for copyright info
==29288== Command: ./test
==29288== Parent PID: 71975
==29288== 
--29288-- 
--29288-- Valgrind options:
--29288--    --leak-check=full
--29288--    --show-leak-kinds=all
--29288--    --track-origins=yes
--29288--    --verbose
--29288--    --log-file=valgrind-out.txt
--29288-- Contents of /proc/version:
--29288--   Linux version 5.15.167.4-microsoft-standard-WSL2 (root@f9c826d3017f) (gcc (GCC) 11.2.0, GNU ld (GNU Binutils) 2.37) #1 SMP Tue Nov 5 00:21:55 UTC 2024
--29288-- 
--29288-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-lzcnt-rdtscp-sse3-ssse3-avx-avx2-bmi-f16c-rdrand-rdseed
--29288-- Page sizes: currently 4096, max supported 4096
--29288-- Valgrind library directory: /usr/libexec/valgrind
--29288-- Reading syms from /my_path/box-shared-test/test
--29288-- Reading syms from /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
--29288--   Considering /usr/lib/debug/.build-id/1c/8db5f83bba514f8fd5f1fb6d7be975be1bb855.debug ..
--29288--   .. build-id is valid
--29288-- Reading syms from /usr/libexec/valgrind/memcheck-amd64-linux
--29288--    object doesn't have a dynamic symbol table
--29288-- Scheduler: using generic scheduler lock implementation.
--29288-- Reading suppressions file: /usr/libexec/valgrind/default.supp
==29288== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-29288-by-user-on-???
==29288== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-29288-by-user-on-???
==29288== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-29288-by-user-on-???
==29288== 
==29288== TO CONTROL THIS PROCESS USING vgdb (which you probably
==29288== don't want to do, unless you know exactly what you're doing,
==29288== or are doing some strange experiment):
==29288==   /usr/bin/vgdb --pid=29288 ...command...
==29288== 
==29288== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==29288==   /path/to/gdb ./test
==29288== and then give GDB the following command
==29288==   target remote | /usr/bin/vgdb --pid=29288
==29288== --pid is optional if only one valgrind process is running
==29288== 
--29288-- REDIR: 0x4028b00 (ld-linux-x86-64.so.2:strlen) redirected to 0x580c2e1a (???)
--29288-- REDIR: 0x40272b0 (ld-linux-x86-64.so.2:index) redirected to 0x580c2e34 (???)
--29288-- Reading syms from /usr/libexec/valgrind/vgpreload_core-amd64-linux.so
--29288-- Reading syms from /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so
==29288== WARNING: new redirection conflicts with existing -- ignoring it
--29288--     old: 0x04028b00 (strlen              ) R-> (0000.0) 0x580c2e1a ???
--29288--     new: 0x04028b00 (strlen              ) R-> (2007.0) 0x0484f340 strlen
--29288-- REDIR: 0x40274e0 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4850460 (strcmp)
--29288-- REDIR: 0x4026910 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4853cd0 (mempcpy)
--29288-- Reading syms from /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.33
--29288-- Reading syms from /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
--29288-- Reading syms from /usr/lib/x86_64-linux-gnu/libc.so.6
--29288--   Considering /usr/lib/debug/.build-id/42/c84c92e6f98126b3e2230ebfdead22c235b667.debug ..
--29288--   .. build-id is valid
--29288-- Reading syms from /usr/lib/x86_64-linux-gnu/libm.so.6
--29288-- REDIR: 0x4028ca0 (ld-linux-x86-64.so.2:strncmp) redirected to 0x484fc90 (strncmp)
--29288-- REDIR: 0x4bce050 (libc.so.6:strnlen) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bce0e0 (libc.so.6:strpbrk) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcc1a0 (libc.so.6:strcmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be53b0 (libc.so.6:wcsnlen) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb290 (libc.so.6:memset) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be4b20 (libc.so.6:wcslen) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4c503c0 (libc.so.6:__memcpy_chk) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb200 (libc.so.6:memrchr) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be5350 (libc.so.6:wcsncpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bca720 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be38e0 (libc.so.6:wcschr) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcc090 (libc.so.6:index) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bce110 (libc.so.6:rindex) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be3990 (libc.so.6:wcscmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb4b0 (libc.so.6:stpncpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bf1eb0 (libc.so.6:wmemchr) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcdef0 (libc.so.6:strncmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb510 (libc.so.6:strcasecmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcd310 (libc.so.6:strcspn) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4be48f0 (libc.so.6:wcscpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcc020 (libc.so.6:strcat) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcddf0 (libc.so.6:strncasecmp_l) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcc110 (libc.so.6:strchrnul) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bca630 (libc.so.6:bcmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcd2a0 (libc.so.6:strcpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb5b0 (libc.so.6:strcasecmp_l) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcdcc0 (libc.so.6:strlen) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcdf90 (libc.so.6:strncpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bf1f30 (libc.so.6:wmemcmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4c504e0 (libc.so.6:__memmove_chk) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
==29288== WARNING: new redirection conflicts with existing -- ignoring it
--29288--     old: 0x04ca19d0 (__memcpy_chk_avx_una) R-> (2030.0) 0x04853dd0 __memcpy_chk
--29288--     new: 0x04ca19d0 (__memcpy_chk_avx_una) R-> (2024.0) 0x04853740 __memmove_chk
--29288-- REDIR: 0x4bcb440 (libc.so.6:stpcpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcafc0 (libc.so.6:memmove) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
==29288== Preferring higher priority redirection:
--29288--     old: 0x04ca1a00 (__memcpy_avx_unalign) R-> (2018.0) 0x04851580 __memcpy_avx_unaligned_erms
--29288--     new: 0x04ca1a00 (__memcpy_avx_unalign) R-> (2018.1) 0x04852d60 memmove
--29288-- REDIR: 0x4bca5b0 (libc.so.6:memchr) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bce2e0 (libc.so.6:strspn) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcb0e0 (libc.so.6:mempcpy) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4bcdd50 (libc.so.6:strncasecmp) redirected to 0x483d1c0 (_vgnU_ifunc_wrapper)
--29288-- REDIR: 0x4ca65f0 (libc.so.6:__strrchr_avx2) redirected to 0x484ed20 (rindex)
--29288-- REDIR: 0x4ca4740 (libc.so.6:__strlen_avx2) redirected to 0x484f220 (strlen)
--29288-- REDIR: 0x4ca12a0 (libc.so.6:__memcmp_avx2_movbe) redirected to 0x4852480 (bcmp)
--29288-- REDIR: 0x4ca5820 (libc.so.6:__strncmp_avx2) redirected to 0x484fab0 (strncmp)
--29288-- REDIR: 0x4bc6650 (libc.so.6:malloc) redirected to 0x48467b0 (malloc)
--29288-- REDIR: 0x4928950 (libstdc++.so.6:operator new[](unsigned long)) redirected to 0x4848550 (operator new[](unsigned long))
--29288-- Warning: cross-CU LIMITATION: some inlined fn names
--29288-- might be shown as UnknownInlinedFun
--29288-- REDIR: 0x4bc6d30 (libc.so.6:free) redirected to 0x4849820 (free)
--29288-- REDIR: 0x49268a0 (libstdc++.so.6:operator delete(void*)) redirected to 0x484a080 (operator delete(void*))
==29288== Mismatched free() / delete / delete []
==29288==    at 0x484A164: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==29288==    by 0x11B1FD: main (in /my_path/box-shared-test/test)
==29288==  Address 0x4e2b080 is 0 bytes inside a block of size 16 alloc'd
==29288==    at 0x48485C3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==29288==    by 0x11B1AE: main (in /my_path/box-shared-test/test)
==29288== 
==29288== 
==29288== HEAP SUMMARY:
==29288==     in use at exit: 0 bytes in 0 blocks
==29288==   total heap usage: 52 allocs, 52 frees, 74,544 bytes allocated
==29288== 
==29288== All heap blocks were freed -- no leaks are possible
==29288== 
==29288== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==29288== 
==29288== 1 errors in context 1 of 1:
==29288== Mismatched free() / delete / delete []
==29288==    at 0x484A164: operator delete(void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==29288==    by 0x11B1FD: main (in /my_path/box-shared-test/test)
==29288==  Address 0x4e2b080 is 0 bytes inside a block of size 16 alloc'd
==29288==    at 0x48485C3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==29288==    by 0x11B1AE: main (in /my_path/box-shared-test/test)
==29288== 
==29288== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)