Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Valgrind reports memory leak in runtime with current beta compiler #138430

Closed
jfrimmel opened this issue Mar 12, 2025 · 3 comments
Closed

Valgrind reports memory leak in runtime with current beta compiler #138430

jfrimmel opened this issue Mar 12, 2025 · 3 comments
Labels
C-bug Category: This is a bug. regression-from-stable-to-beta Performance or correctness regression from stable to beta.

Comments

@jfrimmel
Copy link
Contributor

jfrimmel commented Mar 12, 2025

I maintain the cargo-valgrind-crate, which is a wrapper for running binaries & tests automatically within valgrind for memory analysis. As a sanity check, a periodic job runs some basic smoke tests against the current beta compiler. This smoke test was recently triggered, indicating a regression. In order to outrule the aforementioned tool, I present a reproducer with plain valgrind invocations below.

Code

I tried the default test code generated by cargo new --lib:

pub fn add(left: u64, right: u64) -> u64 {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

I expected to see this happen: when running the tests inside valgrind, no memory errors/leaks should be reported. This can be tested using the following commands:

$ cargo new --lib some-tests
$ cd some-tests
$ cargo +stable test --lib  # run the tests normally
$ valgrind --leak-check=full target/debug/deps/some_tests-fa75b02a65f3d9a8  # now run the test executable within valgrind, adjust hash as necessary
<snip>
==42061== 
==42061== HEAP SUMMARY:
==42061==     in use at exit: 0 bytes in 0 blocks
==42061==   total heap usage: 637 allocs, 637 frees, 77,942 bytes allocated
==42061== 
==42061== All heap blocks were freed -- no leaks are possible
==42061== 
==42061== For lists of detected and suppressed errors, rerun with: -s
==42061== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

There are no leaks reported with the current stable compiler (see version info below).

Instead, this happened: when performing the same commands with the current beta compiler (see version info below), there is a leak reported:

$ cargo +beta test --lib
$ valgrind --leak-check=full target/debug/deps/some_tests-865f7506563a5a65  # adjust the hash as necessary
<snip>
==42409== 
==42409== HEAP SUMMARY:
==42409==     in use at exit: 48 bytes in 1 blocks
==42409==   total heap usage: 638 allocs, 637 frees, 77,968 bytes allocated
==42409== 
==42409== 48 bytes in 1 blocks are possibly lost in loss record 1 of 1
==42409==    at 0x48457A8: malloc (vg_replace_malloc.c:446)
==42409==    by 0x1A5E77: alloc (alloc.rs:96)
==42409==    by 0x1A5E77: alloc_impl (alloc.rs:192)
==42409==    by 0x1A5E77: allocate (alloc.rs:254)
==42409==    by 0x1A5E77: {closure#0}<std::thread::Inner> (sync.rs:484)
==42409==    by 0x1A5E77: 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)
==42409==    by 0x1A5E77: new_uninit<std::thread::Inner> (sync.rs:482)
==42409==    by 0x1A5E77: std::thread::Thread::new (mod.rs:1429)
==42409==    by 0x1A5499: std::thread::current::init_current (current.rs:227)
==42409==    by 0x1AEC23: current_or_unnamed (current.rs:184)
==42409==    by 0x1AEC23: std::sync::mpmc::context::Context::new (context.rs:72)
==42409==    by 0x140CD1: __init (context.rs:43)
==42409==    by 0x140CD1: call_once<fn() -> core::cell::Cell<core::option::Option<std::sync::mpmc::context::Context>>, ()> (function.rs:250)
==42409==    by 0x140CD1: 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)
==42409==    by 0x140CD1: std::sys::thread_local::native::lazy::Storage<T,D>::initialize (lazy.rs:64)
==42409==    by 0x142485: 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)
==42409==    by 0x142485: {closure#0} (mod.rs:94)
==42409==    by 0x142485: 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)
==42409==    by 0x142485: 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)
==42409==    by 0x142485: with<std::sync::mpmc::list::{impl#3}::recv::{closure_env#1}<test::event::CompletedTest>, ()> (context.rs:52)
==42409==    by 0x142485: std::sync::mpmc::list::Channel<T>::recv (list.rs:437)
==42409==    by 0x15BBBF: recv_deadline<test::event::CompletedTest> (mod.rs:1119)
==42409==    by 0x15BBBF: recv_timeout<test::event::CompletedTest> (mod.rs:1051)
==42409==    by 0x15BBBF: recv_timeout<test::event::CompletedTest> (mpsc.rs:905)
==42409==    by 0x15BBBF: run_tests<test::console::run_tests_console::{closure_env#2}> (lib.rs:430)
==42409==    by 0x15BBBF: test::console::run_tests_console (console.rs:323)
==42409==    by 0x1791F6: test::test_main (lib.rs:150)
==42409==    by 0x179B7A: test::test_main_static (lib.rs:172)
==42409==    by 0x13F522: some_tests::main (lib.rs:0)
==42409==    by 0x13F45A: core::ops::function::FnOnce::call_once (function.rs:250)
==42409==    by 0x13F55D: std::sys::backtrace::__rust_begin_short_backtrace (backtrace.rs:152)
==42409== 
==42409== LEAK SUMMARY:
==42409==    definitely lost: 0 bytes in 0 blocks
==42409==    indirectly lost: 0 bytes in 0 blocks
==42409==      possibly lost: 48 bytes in 1 blocks
==42409==    still reachable: 0 bytes in 0 blocks
==42409==         suppressed: 0 bytes in 0 blocks
==42409== 
==42409== For lists of detected and suppressed errors, rerun with: -s
==42409== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Version it worked on

It most recently worked on: Rust 1.85
rustc --version --verbose:

rustc 1.85.0 (4d91de4e4 2025-02-17)
binary: rustc
commit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688
commit-date: 2025-02-17
host: x86_64-unknown-linux-gnu
release: 1.85.0
LLVM version: 19.1.7

Version with regression

rustc +beta --version --verbose:

rustc 1.86.0-beta.5 (b2af9a598 2025-03-08)
binary: rustc
commit-hash: b2af9a598e793de1f65afa6c14f7eb7545332ac1
commit-date: 2025-03-08
host: x86_64-unknown-linux-gnu
release: 1.86.0-beta.5
LLVM version: 19.1.7

Also happens with the current nightly:
rustc +nightly --version --verbose:

rustc 1.87.0-nightly (665025243 2025-03-11)
binary: rustc
commit-hash: 6650252439d4e03368b305c42a10006e36f1545e
commit-date: 2025-03-11
host: x86_64-unknown-linux-gnu
release: 1.87.0-nightly
LLVM version: 20.1.0

@rustbot modify labels: +regression-from-stable-to-beta -regression-untriaged

@jfrimmel jfrimmel added C-bug Category: This is a bug. regression-untriaged Untriaged performance or correctness regression. labels Mar 12, 2025
@rustbot rustbot added I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. regression-from-stable-to-beta Performance or correctness regression from stable to beta. and removed regression-untriaged Untriaged performance or correctness regression. labels Mar 12, 2025
@jfrimmel
Copy link
Contributor Author

Might be a duplicate of #135608.

@jieyouxu
Copy link
Member

I think this is #135608 indeed.

@jieyouxu
Copy link
Member

Closing as a duplicate of #135608, thanks for the report.

@jieyouxu jieyouxu removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Mar 13, 2025
jfrimmel added a commit to jfrimmel/cargo-valgrind that referenced this issue Mar 31, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
## Background
Valgrind may detect leaks in the Rust standard library as it has done in
the past (e.g. [here][rust1.83] and [here][beta]). Those "leaks" report-
ed by Valgrind are not considered as leaks by the Rust team when they
are small permanent allocation, which is not growing (as described
[here][comment-1]) and there is no guarantee, that the Rust standard
library is free of memory leaks (as described [here][comment-2]). There-
fore some reports of Valgrind are entirely non-actionable by a user of
this crate.

In order to solve this, this PR addds a list of suppressions for the
Rust `std`. Those are applied automatically, so that those "leaks" are
never reported to the user. New suppressions should be added when leaks
are detected in the [periodic test against the beta compiler][beta-job],
but only after reporting them to the [Rust team][new-rust-issue] as
documented in the new `suppressions/README.md`.

The implementation is conceptually simple, but a bit tricky in the im-
plementation: the suppression files are added to the `suppressions`-di-
rectory and should be used by `cargo-valgrind` when running. But that
directory is not available at runtime. Therefore the files need to be
embedded into the binary. This is done by two co-working parts:
1.  the build script, which reads all files in the `suppressions`-folder
    and writes their contents to a constant string `SUPPRESSIONS` and
2.  the runtime code which crates a temporary file containing the afore-
    mentioned constant string. This file is then used as the argument
    to the `--suppressions`-option.

[rust1.83]: rust-lang/rust#133574
[beta]: rust-lang/rust#138430
[comment-1]: rust-lang/rust#133574 (comment)
[comment-2]: rust-lang/rust#135608 (comment)
[beta-job]: https://github.com/jfrimmel/cargo-valgrind/actions/workflows/beta.yaml
[new-rust-issue]: https://github.com/rust-lang/rust/issues/new?template=regression.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. regression-from-stable-to-beta Performance or correctness regression from stable to beta.
Projects
None yet
Development

No branches or pull requests

3 participants