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

Leak checker misses pointers cast to integer #1318

Closed
RalfJung opened this issue Apr 10, 2020 · 9 comments · Fixed by #1485
Closed

Leak checker misses pointers cast to integer #1318

RalfJung opened this issue Apr 10, 2020 · 9 comments · Fixed by #1485
Labels
A-intptrcast Area: affects int2ptr and ptr2int casts A-leaks Area: affects the memory leak checker C-bug Category: This is a bug.

Comments

@RalfJung
Copy link
Member

RalfJung commented Apr 10, 2020

The leak checker considers allocations live when they can still be reached from a static. However, this scan can only consider pointers that are still stored as pointer values (with provenance), not pointers cast to an integer (where provenance is lost).

This causes false positives, at least, for

@RalfJung RalfJung added A-intptrcast Area: affects int2ptr and ptr2int casts A-leaks Area: affects the memory leak checker C-bug Category: This is a bug. labels Apr 10, 2020
@RalfJung
Copy link
Member Author

RalfJung commented Apr 10, 2020

For the Windows Mutex, one option on the Miri side is to keep provenance on ptr-to-int casts, and only delete provenance later when the integer is actually used for an integer operation. I am not extremely happy about this but it is also not entirely awful. This is blocked on #841.


For crossbeam, it turns out the pointer is not just cast to an integer, it also has its last bits repurposed to store some flags. So, even if we do the above, we are still going to lose the provenance.

I would say trying to preserve provenance across these bitwise operations is conceptually just not the kind of semantics that Miri should give to Rust. It's also a game of whack-a-mole to get ever more effective at preserving provenance -- a game that the proper support for ptr-int-casts that we have now is supposed to just end, so I do not have a big interest in re-opening that game.

Another option might be to expect libraries that pull such tricks (storing the pointers that keep an allocation live without provenance) to give us some extra hints, via some Miri-specific instrumentation/intrinsics or so. (I recall @oli-obk proposed such operations a while ago for a different purpose and I found the idea quite disgusting. ;) However, the code doing the casting might not even be aware that it is working on data stored in a global static, so it is unclear how such an API could even look like.

Cc @jonhoo

@jonhoo
Copy link

jonhoo commented Apr 11, 2020

I think if there were annotations libraries like crossbeam could give to miri to get tracking to work in these situations, they would be happy to add those :) At least I would be!

vertexclique added a commit to bastion-rs/bastion that referenced this issue May 19, 2020
vertexclique added a commit to bastion-rs/bastion that referenced this issue May 19, 2020
* Hello transactional memory

* Lever version update

* Seems like rust-lang/miri#1318 is not ready yet. Disable leak checks until stabilization

* disable leak checks for all of the tests
@RalfJung
Copy link
Member Author

Some time later, I don't think we have a proposal on the table that is better than asking the library for help. I am imagining an intrinsic like miri_ignore_leak_root or so that takes a pointer, and then the allocation that pointer points to gets added to the list of "roots" that Miri considers when checking if memory is still reachable on program termination (currently, that list consists of all the statics). @oli-obk any thoughts?

What I am not sure about is if the intrinsic should require the pointer to point to the beginning of the allocation -- I think for now I'd like to be as restrictive as possible and make this requirement.

@oli-obk
Copy link
Contributor

oli-obk commented Jun 28, 2020

We don't even need to burden rustc with an intrinsic. We can just use an extern "C" function (which also helps stable code, since it doesn't have to invoke an intrinsic). Basically users would write

#[cfg(miri)]
extern "C" { fn miri_ignore_leak_root(ptr: usize); }
#[cfg(miri)]
unsafe { miri_ignore_leak_root(some_ptr as usize); }

What I am not sure about is if the intrinsic should require the pointer to point to the beginning of the allocation -- I think for now I'd like to be as restrictive as possible and make this requirement.

I agree, we can emit a nice error stating the offset required to obtain a root pointer in order to help users quickly adjust their code to work without having to figure out an earlier site for obtaining the pointer.

If we feel really fancy we can make the leak checker emit a note at the first ptr2intcast site

@RalfJung
Copy link
Member Author

We don't even need to burden rustc with an intrinsic. We can just use an extern "C" function (which also helps stable code, since it doesn't have to invoke an intrinsic).

Oh wow.^^ Now I wonder if we should have used that for miri_start_panic. ;)

I'd make the type a pointer type though, not usize.

@RalfJung
Copy link
Member Author

#1485 provides a solution to this: programs can call the extern "Rust" function miri_static_root to mark an allocation as being a root of static memory that should not be considered leaking when the program terminates.

@jonhoo if you could experiment with using that in crossbeam, that would be amazing. :)

@jonhoo
Copy link

jonhoo commented Jul 23, 2020

@RalfJung That's exciting! Unfortunately I'm pretty swamped these days, but I posted a comment over in crossbeam-rs/crossbeam#464 (comment) to see if I could pique some interest.

Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Jul 24, 2020
 Miri: use extern fn to expose interpreter operations to program; fix leak checker on Windows

This PR realizes an idea that @oli-obk has been suggesting for a while: to use Miri-specific `extern` functions to provide some extra capabilities to the program. Initially, we have two of these methods, which libstd itself needs:
* `miri_start_panic`, which replaces the intrinsic of the same name (mostly for consistency, to avoid having multiple mechanisms for Miri-specific functionality).
* `miri_static_root`, which adds an allocation to a list of static "roots" that Miri considers as not having leaked (including all memory reachable through them). This is needed for rust-lang/miri#1302.

We use `extern` functions instead of intrinsics for this so that user code can more easily call these Miri hoolks -- e.g. `miri_static_root` should be useful for rust-lang/miri#1318.

The Miri side of this is at rust-lang/miri#1485.

r? @oli-obk
bors added a commit to rust-lang-ci/rust that referenced this issue Jul 24, 2020
 Miri: use extern fn to expose interpreter operations to program; fix leak checker on Windows

This PR realizes an idea that @oli-obk has been suggesting for a while: to use Miri-specific `extern` functions to provide some extra capabilities to the program. Initially, we have two of these methods, which libstd itself needs:
* `miri_start_panic`, which replaces the intrinsic of the same name (mostly for consistency, to avoid having multiple mechanisms for Miri-specific functionality).
* `miri_static_root`, which adds an allocation to a list of static "roots" that Miri considers as not having leaked (including all memory reachable through them). This is needed for rust-lang/miri#1302.

We use `extern` functions instead of intrinsics for this so that user code can more easily call these Miri hoolks -- e.g. `miri_static_root` should be useful for rust-lang/miri#1318.

The Miri side of this is at rust-lang/miri#1485.

r? @oli-obk
@bors bors closed this as completed in 91b58c9 Jul 25, 2020
@divergentdave
Copy link
Contributor

I'm working on triaging errors running crossbeam's test suite in Miri. For a typical leak report when using crossbeam-epoch, the only leaking allocations are for two different kinds of linked lists held by the global garbage collection context. Both of these data structures use crossbeam_epoch::Atomic, and the leak report misses these because they are behind a pointer-to-int conversion.

If I understand correctly, calling miri_static_root on entries as they are added would empty out the leak report, at the expense of possibly missing bugs if an entry were popped from the globally held lists and leaked instead of deallocated.

My other ideas, to avoid false negatives:

  • Conditionally add const pointer members to list entry data structures, update them after successfully updating the Atomic members, and never read them. Upon exit, the interpreter will be able to chase these extra pointers down the linked lists, and correctly ignore the entries. Downside: this will require adding conditional compilation blocks throughout the data structures, imposing some maintenance burden.
  • Add another extern fn to register an "atexit" callback, then add one conditional compilation block to call it, and within the callback, chase down the linked lists, calling miri_static_root on each entry. I'm not a big fan of this idea because it adds Turing completeness in a strange place, and I don't know what sort of weird issues would arise from trying to run code after the interpreter has exited main.

@RalfJung
Copy link
Member Author

If I understand correctly, calling miri_static_root on entries as they are added would empty out the leak report, at the expense of possibly missing bugs if an entry were popped from the globally held lists and leaked instead of deallocated.

Correct.

Add another extern fn to register an "atexit" callback, then add one conditional compilation block to call it, and within the callback, chase down the linked lists, calling miri_static_root on each entry. I'm not a big fan of this idea because it adds Turing completeness in a strange place, and I don't know what sort of weird issues would arise from trying to run code after the interpreter has exited main.

Such a mechanism already exists (at least per-thread) since Miri implements TLS dtors run on thread exit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-intptrcast Area: affects int2ptr and ptr2int casts A-leaks Area: affects the memory leak checker C-bug Category: This is a bug.
Projects
None yet
4 participants