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

Add HostAlignedByteCount to enforce alignment at compile time #9620

Merged
merged 2 commits into from
Nov 22, 2024

Conversation

sunshowers
Copy link
Contributor

@sunshowers sunshowers commented Nov 19, 2024

As part of the work to allow mmaps to be backed by other implementations (#9544), I realized that we didn't have any way to track whether a particular usize is host-page-aligned at compile time.

Add a HostAlignedByteCount which tracks that a particular usize is aligned to the host page size. This also does not expose safe unchecked arithmetic operations, to ensure that overflows are always handled.

With HostAlignedByteCount, a lot of runtime checks can go away thanks to the type-level assertion.

In the interest of keeping the diff relatively small, I haven't converted everything over yet. More code can be converted over as we go along.

@sunshowers sunshowers requested a review from a team as a code owner November 19, 2024 02:29
@sunshowers sunshowers requested review from alexcrichton and removed request for a team November 19, 2024 02:29
Comment on lines +161 to +168
let stack_size = self.stack_size.byte_count() - self.page_size.byte_count();
let bottom_of_stack = top - stack_size;
let start_of_stack = bottom_of_stack - self.page_size;
let start_of_stack = bottom_of_stack - self.page_size.byte_count();
assert!(start_of_stack >= base && start_of_stack < (base + len));
assert!((start_of_stack - base) % self.stack_size == 0);
assert!((start_of_stack - base) % self.stack_size.byte_count() == 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code and the similar section below would be good to convert over, but I decided to do this in a followup.

@sunshowers sunshowers force-pushed the host-aligned branch 2 times, most recently from a64e06d to 7546b03 Compare November 19, 2024 02:40
@github-actions github-actions bot added the wasmtime:api Related to the API of the `wasmtime` crate itself label Nov 19, 2024
@sunshowers
Copy link
Contributor Author

I started working on a followup as well: 03efe42

Unfortunately GitHub's review flow does not let you manage stacked PRs unless you have push access to the destination repo, so I'll have to put these up one at a time. Sigh :(

Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

Thanks for this!

crates/wasmtime/src/runtime/vm.rs Outdated Show resolved Hide resolved
crates/wasmtime/src/runtime/vm.rs Outdated Show resolved Hide resolved
crates/wasmtime/src/runtime/vm.rs Outdated Show resolved Hide resolved
crates/wasmtime/src/runtime/vm.rs Outdated Show resolved Hide resolved
@alexcrichton
Copy link
Member

As a heads up I'm adding #9614 to the merge queue which is going to have a number of conflicts with this. It should (hopefully) be just a few #[cfg(feature = "signals-based-traps")] in a few places but if anything is overly difficult or onerous lemme know and I can help out

sunshowers added a commit to sunshowers/wasmtime that referenced this pull request Nov 20, 2024
…maps

In the wasmtime codebase there are two kinds of mmaps: those that are always
backed by anonymous memory and are always aligned, and those that are possibly
file-backed and so the length might be unaligned. In
bytecodealliance#9620 I accidentally mixed the
two up in a way that was a ticking time bomb.

To resolve this, add a type parameter to `Mmap` to distinguish between the
cases. All of wasmtime's users are clearly either one or the other, so it's
quite simple in the end.
github-merge-queue bot pushed a commit that referenced this pull request Nov 20, 2024
…maps (#9639)

In the wasmtime codebase there are two kinds of mmaps: those that are always
backed by anonymous memory and are always aligned, and those that are possibly
file-backed and so the length might be unaligned. In
#9620 I accidentally mixed the
two up in a way that was a ticking time bomb.

To resolve this, add a type parameter to `Mmap` to distinguish between the
cases. All of wasmtime's users are clearly either one or the other, so it's
quite simple in the end.
@sunshowers sunshowers force-pushed the host-aligned branch 2 times, most recently from 9070ae2 to ef285a4 Compare November 20, 2024 22:47
@sunshowers
Copy link
Contributor Author

(Apologies for the force push -- had to do it as part of the rebase. GH makes me cry)

Copy link
Member

@alexcrichton alexcrichton left a comment

Choose a reason for hiding this comment

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

Thanks again for this!

@alexcrichton alexcrichton added this pull request to the merge queue Nov 21, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 21, 2024
@sunshowers
Copy link
Contributor Author

All right, think I fixed the Miri issue.

@alexcrichton alexcrichton added this pull request to the merge queue Nov 21, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Nov 21, 2024

// If the Wasm memory's page size is smaller than the host's page
// size, then we might not need to actually change permissions,
// since we are forced to round our accessible range up to the
// host's page size.
if new_accessible > self.accessible() {
if let Ok(difference) = new_accessible.checked_sub(self.accessible()) {
Copy link
Member

Choose a reason for hiding this comment

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

I think this may be the source of the bug, albeit it's a bit indirect. Previously the > condition is now morally becoming >= since new_accessible == self.accessible() didn't run this block before but it's now running this block with difference == 0. I think then it may be the case that mprotect for 0 bytes is succeeding on unix but failing on Windows.

On Linux I see mprotect(0x740d36001000, 0, PROT_READ|PROT_WRITE) = 0 in strace with this patch when running the failing spec test and while I haven't executed this on Windows this is what I would suspect is going on.

Another option is to update the Mmap type to skip make_accessible when the byte size is zero which I think would also be reasonable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's terrifying -- I'm going to fix this (probably by making make_accessible ignore zero-sized regions) and go through this code again to make sure I didn't miss any other sign changes. Thanks for diagnosing this!

Copy link
Contributor Author

@sunshowers sunshowers Nov 22, 2024

Choose a reason for hiding this comment

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

Made make_accessible a no-op on zero-length allocations, as well as the other mprotect calls (make_executable and make_readonly). They also had this as a lurking bug because they had assertions that range.start <= range.end, not range.start < range.end.

I've also added tests for zero-length calls, and re-reviewed all of the sign changes -- didn't see any other errors.

As part of the work to allow mmaps to be backed by other implementations, I
realized that we didn't have any way to track whether a particular usize is
host-page-aligned at compile time.

Add a `HostAlignedByteCount` which tracks that a particular usize is aligned to
the host page size. This also does not expose safe unchecked arithmetic
operations, to ensure that overflows always error out.

With `HostAlignedByteCount`, a lot of runtime checks can go away thanks to the
type-level assertion.

In the interest of keeping the diff relatively small, I haven't converted
everything over yet. More can be converted over as time permits.
@alexcrichton alexcrichton added this pull request to the merge queue Nov 22, 2024
Merged via the queue into bytecodealliance:main with commit 74a703b Nov 22, 2024
40 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wasmtime:api Related to the API of the `wasmtime` crate itself
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants