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

don't report memory leaks for static data #940

Closed
matklad opened this issue Sep 1, 2019 · 22 comments · Fixed by #1301
Closed

don't report memory leaks for static data #940

matklad opened this issue Sep 1, 2019 · 22 comments · Fixed by #1301
Labels
A-interpreter Area: affects the core interpreter A-leaks Area: affects the memory leak checker C-bug Category: This is a bug.

Comments

@matklad
Copy link
Member

matklad commented Sep 1, 2019

Currently, miri reports a memory leak for a static with heap allocated data. This is somewhat expected, as statics don't run destructors and indeed leak memory. However, this is not error, and ideally shouldn't be reported by miri as such.

See matklad/once_cell@327f4e2 for an example.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 1, 2019

Oh, heh. I never thought about heap pointers being leaked into statics. Also calls to Box::leak should probably not trigger the leak errors.

Maybe we should, before running the check, recursively mark anything reachable by a static as static itself.

And we should add a hook to Box::leak to make it mark its memory as static (we can use rustc_diagnostic_item to get the DefId of Box::leak

@RalfJung RalfJung added A-interpreter Area: affects the core interpreter C-bug Category: This is a bug. labels Sep 15, 2019
@RalfJung
Copy link
Member

We probably also should make the leak check optional. A quick solution would be -Zmiri-allow-leaks; or we could make the leak error a "deny-by-default lint" and then use -Aleak or so.

And we should add a hook to Box::leak to make it mark its memory as static

Why that? I think our leak check should trigger for Box::leak memory that is actually leaked.

@oli-obk
Copy link
Contributor

oli-obk commented Sep 16, 2019

Why that? I think our leak check should trigger for Box::leak memory that is actually leaked.

I assumed that Box::leak could never be undone, but even stacked borrows is fine with it: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cae54e950dfd613b3c255263f352795f

I'm assuming &'static mut T and *mut T are equal in the eyes of stacked borrows

My statement came from assuming that people don't use Box::leak and expect to ever take back ownership of that. If you ever stored it in a global it seems impossible to ever safely undo the leak because any other code may have an immutable reference to the thing.

Nixing the entire leak check, just because some code found it ok to leak a single value out of lazy static like reasons, seems a bit extreme.

@RalfJung
Copy link
Member

I'm assuming &'static mut T and *mut T are equal in the eyes of stacked borrows

No they are not (the former disallows accesses through other aliases).

My statement came from assuming that people don't use Box::leak and expect to ever take back ownership of that. If you ever stored it in a global it seems impossible to ever safely undo the leak because any other code may have an immutable reference to the thing.

I see. It would seem odd though, I think, to enshrine this in the operational semantics.

Nixing the entire leak check, just because some code found it ok to leak a single value out of lazy static like reasons, seems a bit extreme.

We could also have a magic crazy intrinsic "tell Miri this here is allowed to leak"... I think I will regret suggesting this, though. ;)

@RalfJung
Copy link
Member

RalfJung commented Nov 23, 2019

I just ran into another instance of this. The following program is considered leaking:

fn main() {
    let _val = 0;
    // Capture something to make closure a non-ZST
    std::panic::set_hook(Box::new(move |_info| { _val; }));
}

jonhoo referenced this issue in jonhoo/flurry Mar 17, 2020
These are probably spurious; see
crossbeam-rs/crossbeam#464
and
spacejam/sled#937 (comment)

And besides, we're now running both the leak sanitizer and the address
sanitizer.
@RalfJung
Copy link
Member

On Windows, we need this even to get an empty fn main() {} working with the leak checker enabled: there are some statics in libstd that are otherwise considered leaking.

@bors bors closed this as completed in 325682a Apr 7, 2020
jonhoo added a commit to jonhoo/flurry that referenced this issue Apr 10, 2020
Now that rust-lang/miri#940 has been fixed,
and has landed in nightly
(rust-lang/rust#70897), we should be able to run
miri with leak check enabled again!
bors bot added a commit to matklad/once_cell that referenced this issue Apr 10, 2020
99: miri: enable leakcheck r=matklad a=RalfJung

rust-lang/miri#940 is fixed, so we should be able to enable the leak checker now. :)

(However, we'll have to wait until rust-lang/rust#70897 lands so that this is available via rustup.)

Co-authored-by: Ralf Jung <post@ralfj.de>
@jonhoo
Copy link

jonhoo commented Apr 10, 2020

Hmm, I still get a reported leak through crossbeam-epoch when using statics. I don't know enough about the crossbeam internals to say whether it is legitimate or not, but I know they run a leak sanitizer which hasn't caught anything? I haven't tried running the crossbeam tests themselves on miri yet, but maybe that'd be worth doing?

@RalfJung
Copy link
Member

@jonhoo could that be the same problem as #1302, i.e., the pointer is cast to an integer before being stored and thus Miri cannot "see" it and cannot figure out that the allocation is reachable?

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

Ah, quite possible! I think crossbeam-utils does that in a few places from skimming the code. Sounds like maybe it'd be worthwhile to change those to use raw pointers instead. I don't know why the code is using usize in the first place..

@RalfJung
Copy link
Member

They might also be using AtomicPtr which under the hood does a cast to usize -- see rust-lang/rust#70765.

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

I suspect the offender here might be
https://github.com/crossbeam-rs/crossbeam/blob/0599b8e88cccc88525cd430c806b057cc4be2829/crossbeam-epoch/src/atomic.rs#L128

I suspect it was stored as a usize due to using alignment bits to store a tag:
https://github.com/crossbeam-rs/crossbeam/blob/0599b8e88cccc88525cd430c806b057cc4be2829/crossbeam-epoch/src/atomic.rs#L120-L122

Is that also legal for * pointers?

crossbeam-channel and crossbeam-deque both use AtomicPtr, though none of that is used in the code that reports the leak I linked to.

@RalfJung
Copy link
Member

Is that also legal for * pointers?

It is only legal for raw pointers -- references must be aligned, so their last bits must be 0.

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

Sorry, yes, I meant the "also" in comparison to usize, not in comparison to references which I know must be aligned :)

@RalfJung
Copy link
Member

RalfJung commented Apr 10, 2020

Ah, yes raw pointers may be NULL, dangling and unaligned, that is fine, as long as they do not get dereferenced (* operator -- even when no memory access happens).

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

Digging some more into this, it's actually tricky to make crossbeam use AtomicPtr since it relies on operations like AtomicUsize::fetch_xor, which do not exist for AtomicPtr :/

@RalfJung
Copy link
Member

Yeah, that sounds tricky indeed. Hm.

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

Especially since it seems these were once supported, and then removed: rust-lang/rust#10154

jonhoo added a commit to jonhoo/crossbeam that referenced this issue Apr 10, 2020
Pointers stored as `usize` tend to cause miri to lose pointer provenance
tracking, which means we can't take advantage of its checking! See also
the discussion at
rust-lang/miri#940 (comment).

This does not yet compile since `AtomicPtr` does not have `fetch_*`
methods. They were added and then removed from the standard library back
in the day (rust-lang/rust#10154), but I think
the reason they were removed has now been remedied, so they will
hopefully come back again!
@RalfJung
Copy link
Member

Honestly I am not sure any of this will suffice. XOR is fundamentally an integer operation that loses provenance, and while we could re-introduce awful hacks in Miri that enable provenance tracking over XOR, I do not think that is a good idea.

@RalfJung
Copy link
Member

See ptr_int_arithmetic removed in this commit for the awful things we used to do.

@jonhoo
Copy link

jonhoo commented Apr 10, 2020

crossbeam only specifically does xor of the alignment bits though, not of the "real" part of the pointer. Though I recognize that that might not really help 😅 It would be really sad if miri simply could not support checking crossbeam-epoch, or anything that depends on it.

@RalfJung
Copy link
Member

It would be really sad if miri simply could not support checking crossbeam-epoch, or anything that depends on it.

Yeah, I agree that would be sad.
However, re-adding that code might be even more sad.^^

@RalfJung RalfJung added the A-leaks Area: affects the memory leak checker label Apr 10, 2020
@RalfJung
Copy link
Member

To avoid continuing this discussion in a closed issue, I opened #1318.

jonhoo added a commit to jonhoo/flurry that referenced this issue May 6, 2020
Now that rust-lang/miri#940 has been fixed,
and has landed in nightly
(rust-lang/rust#70897), we should be able to run
miri with leak check enabled again!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-interpreter Area: affects the core interpreter A-leaks Area: affects the memory leak checker C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants