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

store futexes in per-allocation data rather than globally #3971

Merged
merged 1 commit into from
Nov 10, 2024

Conversation

RalfJung
Copy link
Member

@RalfJung RalfJung commented Oct 14, 2024

This is a change in behavior. We used to index futexes entirely on their absolute address, so they stuck around even if the allocation backing that address disappears -- in fact one could signal FUTEX_WAKE on unallocated memory. That works fine on Linux but is quite odd from a Rust AM perspective. It also means Miri has a leak, as some futex state never disappears.

So this PR attaches the futex to the "virtual" address inside an allocation, meaning it disappears when the allocation is freed. Strictly speaking this means we will miss wakeups if the address gets reused, and then someone does FUTEX_WAKE and expects to wake up a thread that started blocking on the old address before it got deallocated... but that seems really contrived?

@rust-lang/miri what do you think?

@bors
Copy link
Contributor

bors commented Oct 14, 2024

☔ The latest upstream changes (presumably #3973) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Oct 14, 2024

☔ The latest upstream changes (presumably #3974) made this pull request unmergeable. Please resolve the merge conflicts.

@saethlin
Copy link
Member

saethlin commented Oct 15, 2024

in fact one could signal FUTEX_WAKE on unallocated memory. That works fine on Linux but is quite odd from a Rust AM perspective

I don't mind declaring such cases Too Weird For Rust (or Miri). Same goes for reusing addresses; if someone comes up with an actual use case for these we can consider figuring it out then. But that seems unlikely.

@RalfJung
Copy link
Member Author

Cc @m-ou-se who did the original implementation here and made it work on al (even dangling) addresses.

@m-ou-se
Copy link
Member

m-ou-se commented Oct 19, 2024

The only thing that's important is that it doesn't segfault/crash/panic/do anything bad when calling futex_wake on a futex that no longer exists.

So, something like:

let b = Box::new(AtomicU32::new(0));
let p = b.as_ptr();
// ...
drop(b);
// ...
futex_wake(p, 1); // "use after free" is perfectly fine for futex_wake!

This should just do nothing (or spuriously wake another futex, whatever).

@@ -35,17 +35,6 @@ fn wake_nobody() {
}
}

fn wake_dangling() {
Copy link
Member

@m-ou-se m-ou-se Oct 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should still work. At least parking_lot depends on "wake after free" to be acceptable.

Edit: or it should return -1 with errno set to EFAULT, treating deallocated memory as unmapped memory.

@m-ou-se
Copy link
Member

m-ou-se commented Oct 19, 2024

From parking_lot_core:

        // The thread data may have been freed at this point, but it doesn't
        // matter since the syscall will just return EFAULT in that case.
        let r = libc::syscall(
            libc::SYS_futex,
            self.futex,
            libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
            1,
        );

Note that the linux kernel returns EFAULT when the memory is unmapped. In most cases, memory freed by the global allocator is still mapped (so the kernel doesn't know it's deallocated), but it seems perfectly fine for miri to just always return EFAULT if the allocation is gone.

@RalfJung
Copy link
Member Author

Returning EFAULT would for the first time let a program "test" whether a given address is allocated. But maybe we should still do it.

@m-ou-se
Copy link
Member

m-ou-se commented Oct 22, 2024

Returning EFAULT would for the first time let a program "test" whether a given address is allocated. But maybe we should still do it.

Yeah. It's more accurate to just return success if the memory is deallocated, without waking up anything. I don't think it matters much.

@RalfJung
Copy link
Member Author

RalfJung commented Nov 9, 2024

It's more accurate to just return success if the memory is deallocated, without waking up anything.

Yeah I think I prefer that.

EDIT: After thinking about it some more, I think I prefer the EFAULT variant. That makes it more likely that someone will file a bug report if this behavior for some reason doesn't work for them.

@RalfJung RalfJung force-pushed the futex-virtual branch 3 times, most recently from d039db8 to d300f7d Compare November 9, 2024 10:47
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Nov 9, 2024
interpret: get_alloc_info: also return mutability

This will be needed for rust-lang/miri#3971

This then tuned into a larger refactor where we introduce a new type for the `get_alloc_info` return data, and we move some code to methods on `GlobalAlloc` to avoid duplicating it between the validity check and `get_alloc_info`.
workingjubilee added a commit to workingjubilee/rustc that referenced this pull request Nov 10, 2024
interpret: get_alloc_info: also return mutability

This will be needed for rust-lang/miri#3971

This then tuned into a larger refactor where we introduce a new type for the `get_alloc_info` return data, and we move some code to methods on `GlobalAlloc` to avoid duplicating it between the validity check and `get_alloc_info`.
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Nov 10, 2024
Rollup merge of rust-lang#132801 - RalfJung:alloc-mutability, r=oli-obk

interpret: get_alloc_info: also return mutability

This will be needed for rust-lang/miri#3971

This then tuned into a larger refactor where we introduce a new type for the `get_alloc_info` return data, and we move some code to methods on `GlobalAlloc` to avoid duplicating it between the validity check and `get_alloc_info`.
RalfJung pushed a commit to RalfJung/miri that referenced this pull request Nov 10, 2024
interpret: get_alloc_info: also return mutability

This will be needed for rust-lang#3971

This then tuned into a larger refactor where we introduce a new type for the `get_alloc_info` return data, and we move some code to methods on `GlobalAlloc` to avoid duplicating it between the validity check and `get_alloc_info`.
@rustbot

This comment has been minimized.

@RalfJung
Copy link
Member Author

I've also changed the ordering of the read in futex_wait to be "acquire" -- it only seems reasonable that if we observe a value this way, we actually establish proper ordering with it. (In practice this is a syscall so should definitely have this "acquire" effect.)

@RalfJung RalfJung force-pushed the futex-virtual branch 2 times, most recently from 3609e56 to 242aff9 Compare November 10, 2024 09:58
@RalfJung RalfJung enabled auto-merge November 10, 2024 10:02
@RalfJung RalfJung added this pull request to the merge queue Nov 10, 2024
Merged via the queue into rust-lang:master with commit f3cdb0b Nov 10, 2024
7 checks passed
@RalfJung RalfJung deleted the futex-virtual branch November 10, 2024 10:51
mati865 pushed a commit to mati865/rust that referenced this pull request Nov 12, 2024
interpret: get_alloc_info: also return mutability

This will be needed for rust-lang/miri#3971

This then tuned into a larger refactor where we introduce a new type for the `get_alloc_info` return data, and we move some code to methods on `GlobalAlloc` to avoid duplicating it between the validity check and `get_alloc_info`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants