Skip to content

Valgrind shows memory leak with possibly lost when running tests with cargo test #141084

Closed as duplicate of#135608
@nablaa

Description

@nablaa

When running cargo test under valgrind will full leak check, valgrind shows a memory leak backtrace with "possibly lost" bytes.

This happens with the default template code generated by cargo new / cargo init and affects stable rust versions starting from 1.86.0. No memory leak is observed with older 1.85.0.

Repro:

cargo init --lib

echo No memleak with 1.85
valgrind --error-exitcode=42 --tool=memcheck --leak-check=full $(cargo +1.85.0 test --no-run --message-format=json | jq  -r 'select(.reason=="compiler-artifact") | .executable | select(. != null)')

echo Memleak with 1.86
valgrind --error-exitcode=42 --tool=memcheck --leak-check=full $(cargo +1.86.0 test --no-run --message-format=json | jq  -r 'select(.reason=="compiler-artifact") | .executable | select(. != null)')

echo Memleak with latest nightly
valgrind --error-exitcode=42 --tool=memcheck --leak-check=full $(cargo +nightly test --no-run --message-format=json | jq  -r 'select(.reason=="compiler-artifact") | .executable | select(. != null)')

I expected to see this happen: No memory leaks reported.

Instead, this happened: Memory leaks reported for 1.86.0 as well as the latest nightly:

No memleak with 1.85:

    Creating library package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

   Compiling foo v0.1.0 (/tmp/foo)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s
==866796== Memcheck, a memory error detector
==866796== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==866796== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==866796== Command: /tmp/foo/target/debug/deps/foo-60a4b72f22d3bfbc
==866796==

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s

==866796==
==866796== HEAP SUMMARY:
==866796==     in use at exit: 0 bytes in 0 blocks
==866796==   total heap usage: 638 allocs, 638 frees, 77,348 bytes allocated
==866796==
==866796== All heap blocks were freed -- no leaks are possible
==866796==
==866796== For lists of detected and suppressed errors, rerun with: -s
==866796== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Memleak with 1.86:

Memleak with 1.86
   Compiling foo v0.1.0 (/tmp/foo)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s
==866870== Memcheck, a memory error detector
==866870== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==866870== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==866870== Command: /tmp/foo/target/debug/deps/foo-c47a447f412d7bfc
==866870==

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s

==866870==
==866870== HEAP SUMMARY:
==866870==     in use at exit: 48 bytes in 1 blocks
==866870==   total heap usage: 639 allocs, 638 frees, 77,374 bytes allocated
==866870==
==866870== 48 bytes in 1 blocks are possibly lost in loss record 1 of 1
==866870==    at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==866870==    by 0x1830A7: alloc (alloc.rs:96)
==866870==    by 0x1830A7: alloc_impl (alloc.rs:192)
==866870==    by 0x1830A7: allocate (alloc.rs:254)
==866870==    by 0x1830A7: {closure#0}<std::thread::Inner> (sync.rs:484)
==866870==    by 0x1830A7: allocate_for_layout<core::mem::maybe_uninit::MaybeUninit<std::thread::Inner>, alloc::sync::{impl#14}::new_uninit::{closure_env#0}<std::thread::Inner>, fn(*mut u8) -> *mut alloc::sync::ArcInner<core::mem::maybe_uninit::MaybeUninit<std::thread::Inner>>> (sync.rs:1952)
==866870==    by 0x1830A7: new_uninit<std::thread::Inner> (sync.rs:482)
==866870==    by 0x1830A7: std::thread::Thread::new (mod.rs:1429)
==866870==    by 0x11B719: std::thread::current::init_current (current.rs:227)
==866870==    by 0x11BC53: current_or_unnamed (current.rs:184)
==866870==    by 0x11BC53: std::sync::mpmc::context::Context::new (context.rs:72)
==866870==    by 0x11A111: __init (context.rs:43)
==866870==    by 0x11A111: call_once<fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, ()> (function.rs:250)
==866870==    by 0x11A111: unwrap_or_else<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>> (option.rs:1023)
==866870==    by 0x11A111: std::sys::thread_local::native::lazy::Storage<T,D>::initialize (lazy.rs:64)
==866870==    by 0x120E05: get_or_init<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, (), fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>> (lazy.rs:56)
==866870==    by 0x120E05: {closure#0} (mod.rs:94)
==866870==    by 0x120E05: call_once<std::sync::mpmc::context::{impl#0}::with::CONTEXT::{constant#0}::{closure_env#0}, (core::option::Option<&mut core::option::Option<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>>>)> (function.rs:250)
==866870==    by 0x120E05: try_with<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, std::sync::mpmc::context::{impl#0}::with::{closure_env#1}<std::sync::mpmc::list::{impl#3}::recv::{closure_env#1}<test::event::CompletedTest>, ()>, ()> (local.rs:309)
==866870==    by 0x120E05: with<std::sync::mpmc::list::{impl#3}::recv::{closure_env#1}<test::event::CompletedTest>, ()> (context.rs:52)
==866870==    by 0x120E05: std::sync::mpmc::list::Channel<T>::recv (list.rs:437)
==866870==    by 0x13949F: recv_deadline<test::event::CompletedTest> (mod.rs:1119)
==866870==    by 0x13949F: recv_timeout<test::event::CompletedTest> (mod.rs:1051)
==866870==    by 0x13949F: recv_timeout<test::event::CompletedTest> (mpsc.rs:905)
==866870==    by 0x13949F: run_tests<test::console::run_tests_console::{closure_env#2}> (lib.rs:430)
==866870==    by 0x13949F: test::console::run_tests_console (console.rs:323)
==866870==    by 0x156AD6: test::test_main (lib.rs:150)
==866870==    by 0x15745A: test::test_main_static (lib.rs:172)
==866870==    by 0x11D8D2: foo::main (lib.rs:0)
==866870==    by 0x11D7AA: core::ops::function::FnOnce::call_once (function.rs:250)
==866870==    by 0x11DF1D: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152)
==866870==
==866870== LEAK SUMMARY:
==866870==    definitely lost: 0 bytes in 0 blocks
==866870==    indirectly lost: 0 bytes in 0 blocks
==866870==      possibly lost: 48 bytes in 1 blocks
==866870==    still reachable: 0 bytes in 0 blocks
==866870==         suppressed: 0 bytes in 0 blocks
==866870==
==866870== For lists of detected and suppressed errors, rerun with: -s
==866870== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Memleak with latest nightly:

Memleak with latest nightly
   Compiling foo v0.1.0 (/tmp/foo)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.10s
==866946== Memcheck, a memory error detector
==866946== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==866946== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
==866946== Command: /tmp/foo/target/debug/deps/foo-9e68f113d655f0c4
==866946==

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.07s

==866946==
==866946== HEAP SUMMARY:
==866946==     in use at exit: 48 bytes in 1 blocks
==866946==   total heap usage: 644 allocs, 643 frees, 77,544 bytes allocated
==866946==
==866946== 48 bytes in 1 blocks are possibly lost in loss record 1 of 1
==866946==    at 0x4846828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==866946==    by 0x1A6047: alloc (alloc.rs:93)
==866946==    by 0x1A6047: alloc_impl (alloc.rs:188)
==866946==    by 0x1A6047: allocate (alloc.rs:249)
==866946==    by 0x1A6047: {closure#0}<std::thread::Inner> (sync.rs:505)
==866946==    by 0x1A6047: allocate_for_layout<core::mem::maybe_uninit::MaybeUninit<std::thread::Inner>, alloc::sync::{impl#14}::new_uninit::{closure_env#0}<std::thread::Inner>, fn(*mut u8) -> *mut alloc::sync::ArcInner<core::mem::maybe_uninit::MaybeUninit<std::thread::Inner>>> (sync.rs:1985)
==866946==    by 0x1A6047: new_uninit<std::thread::Inner> (sync.rs:503)
==866946==    by 0x1A6047: std::thread::Thread::new (mod.rs:1429)
==866946==    by 0x1A5609: std::thread::current::init_current (current.rs:227)
==866946==    by 0x1AEA1F: current_or_unnamed (current.rs:184)
==866946==    by 0x1AEA1F: std::sync::mpmc::context::Context::new (context.rs:72)
==866946==    by 0x141E31: __init (context.rs:43)
==866946==    by 0x141E31: call_once<fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, ()> (function.rs:250)
==866946==    by 0x141E31: unwrap_or_else<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>> (option.rs:1048)
==866946==    by 0x141E31: std::sys::thread_local::native::lazy::Storage<T,D>::initialize (lazy.rs:64)
==866946==    by 0x14348B: get_or_init<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, (), fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>> (lazy.rs:56)
==866946==    by 0x14348B: {closure#0} (mod.rs:94)
==866946==    by 0x14348B: call_once<std::sync::mpmc::context::{impl#0}::with::CONTEXT::{constant#0}::{closure_env#0}, (core::option::Option<&mut core::option::Option<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>>>)> (function.rs:250)
==866946==    by 0x14348B: try_with<core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, std::sync::mpmc::context::{impl#0}::with::{closure_env#1}<std::sync::mpmc::list::{impl#3}::recv::{closure_env#1}<test::event::CompletedTest>, ()>, ()> (local.rs:314)
==866946==    by 0x14348B: with<std::sync::mpmc::list::{impl#3}::recv::{closure_env#1}<test::event::CompletedTest>, ()> (context.rs:52)
==866946==    by 0x14348B: std::sync::mpmc::list::Channel<T>::recv (list.rs:442)
==866946==    by 0x15CDF5: recv_deadline<test::event::CompletedTest> (mod.rs:1119)
==866946==    by 0x15CDF5: recv_timeout<test::event::CompletedTest> (mod.rs:1051)
==866946==    by 0x15CDF5: recv_timeout<test::event::CompletedTest> (mpsc.rs:905)
==866946==    by 0x15CDF5: run_tests<test::console::run_tests_console::{closure_env#2}> (lib.rs:441)
==866946==    by 0x15CDF5: test::console::run_tests_console (console.rs:323)
==866946==    by 0x179CA0: test_main_with_exit_callback<test::test_main::{closure_env#0}> (lib.rs:160)
==866946==    by 0x179CA0: test::test_main (lib.rs:101)
==866946==    by 0x17A4FA: test::test_main_static (lib.rs:183)
==866946==    by 0x13F862: foo::main (lib.rs:0)
==866946==    by 0x13F98A: core::ops::function::FnOnce::call_once (function.rs:250)
==866946==    by 0x13F80D: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152)
==866946==
==866946== LEAK SUMMARY:
==866946==    definitely lost: 0 bytes in 0 blocks
==866946==    indirectly lost: 0 bytes in 0 blocks
==866946==      possibly lost: 48 bytes in 1 blocks
==866946==    still reachable: 0 bytes in 0 blocks
==866946==         suppressed: 0 bytes in 0 blocks
==866946==
==866946== For lists of detected and suppressed errors, rerun with: -s
==866946== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Meta

rustc +1.86.0 --version --verbosee:

rustc 1.86.0 (05f9846f8 2025-03-31)
binary: rustc
commit-hash: 05f9846f893b09a1be1fc8560e33fc3c815cfecb
commit-date: 2025-03-31
host: x86_64-unknown-linux-gnu
release: 1.86.0
LLVM version: 19.1.7

rustc +nightly --version --verbosee:

rustc 1.89.0-nightly (d97326eab 2025-05-15)
binary: rustc
commit-hash: d97326eabfc3b2c33abcb08d6bc117aefa697cb7
commit-date: 2025-05-15
host: x86_64-unknown-linux-gnu
release: 1.89.0-nightly
LLVM version: 20.1.4

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-valgrind-full-leakArea: memory leaks detected by valgrind leak-check=fullC-discussionCategory: Discussion or questions that doesn't represent real issues.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions