Skip to content

Add guard pages to the front of linear memories - #2977

Merged
alexcrichton merged 9 commits into
bytecodealliance:mainfrom
alexcrichton:pre-guard
Jun 18, 2021
Merged

Add guard pages to the front of linear memories#2977
alexcrichton merged 9 commits into
bytecodealliance:mainfrom
alexcrichton:pre-guard

Conversation

@alexcrichton

Copy link
Copy Markdown
Member

This commit implements a safety feature for Wasmtime to place guard
pages before the allocation of all linear memories. Guard pages placed
after linear memories are typically present for performance (at least)
because it can help elide bounds checks. Guard pages before a linear
memory, however, are never strictly needed for performance or features.
The intention of a preceding guard page is to help insulate against bugs
in Cranelift or other code generators, such as CVE-2021-32629.

This commit adds a Config::guard_before_linear_memory configuration
option, defaulting to true, which indicates whether guard pages should
be present both before linear memories as well as afterwards. Guard
regions continue to be controlled by
{static,dynamic}_memory_guard_size methods.

The implementation here affects both on-demand allocated memories as
well as the pooling allocator for memories. For on-demand memories this
adjusts the size of the allocation as well as adjusts the calculations
for the base pointer of the wasm memory. For the pooling allocator this
will place a singular extra guard region at the very start of the
allocation for memories. Since linear memories in the pooling allocator
are contiguous every memory already had a preceding guard region in
memory, it was just the previous memory's guard region afterwards. Only
the first memory needed this extra guard.

I've attempted to write some tests to help test all this, but this is
all somewhat tricky to test because the settings are pretty far away
from the actual behavior. I think, though, that the tests added here
should help cover various use cases and help us have confidence in
tweaking the various Config settings beyond their defaults.

Note that this also contains a semantic change where
InstanceLimits::memory_reservation_size has been removed. Instead this
field is now inferred from the static_memory_maximum_size and guard
size settings. This should hopefully remove some duplication in these
settings, canonicalizing on the guard-size/static-size settings as the
way to control memory sizes and virtual reservations.

@github-actions github-actions Bot added fuzzing Issues related to our fuzzing infrastructure wasmtime:api Related to the API of the `wasmtime` crate itself labels Jun 10, 2021
@github-actions

Copy link
Copy Markdown

Subscribe to Label Action

cc @fitzgen, @peterhuene

Details This issue or pull request has been labeled: "fuzzing", "wasmtime:api"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: fuzzing
  • peterhuene: wasmtime:api

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Comment thread crates/runtime/src/instance/allocator/pooling.rs
Comment thread crates/runtime/src/instance/allocator/pooling/uffd.rs Outdated
Comment thread crates/runtime/src/memory.rs Outdated
Comment thread tests/all/memory.rs Outdated
Comment thread tests/all/memory.rs Outdated
Comment thread tests/all/memory.rs Outdated

@peterhuene peterhuene left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks great!

Just the one question about what to do regarding 32-bit default values for static memory sizes and a few nits.

This commit implements a safety feature for Wasmtime to place guard
pages before the allocation of all linear memories. Guard pages placed
after linear memories are typically present for performance (at least)
because it can help elide bounds checks. Guard pages before a linear
memory, however, are never strictly needed for performance or features.
The intention of a preceding guard page is to help insulate against bugs
in Cranelift or other code generators, such as CVE-2021-32629.

This commit adds a `Config::guard_before_linear_memory` configuration
option, defaulting to `true`, which indicates whether guard pages should
be present both before linear memories as well as afterwards. Guard
regions continue to be controlled by
`{static,dynamic}_memory_guard_size` methods.

The implementation here affects both on-demand allocated memories as
well as the pooling allocator for memories. For on-demand memories this
adjusts the size of the allocation as well as adjusts the calculations
for the base pointer of the wasm memory. For the pooling allocator this
will place a singular extra guard region at the very start of the
allocation for memories. Since linear memories in the pooling allocator
are contiguous every memory already had a preceding guard region in
memory, it was just the previous memory's guard region afterwards. Only
the first memory needed this extra guard.

I've attempted to write some tests to help test all this, but this is
all somewhat tricky to test because the settings are pretty far away
from the actual behavior. I think, though, that the tests added here
should help cover various use cases and help us have confidence in
tweaking the various `Config` settings beyond their defaults.

Note that this also contains a semantic change where
`InstanceLimits::memory_reservation_size` has been removed. Instead this
field is now inferred from the `static_memory_maximum_size` and guard
size settings. This should hopefully remove some duplication in these
settings, canonicalizing on the guard-size/static-size settings as the
way to control memory sizes and virtual reservations.
Makes the pooling allocator a bit more reasonable by default on 32-bit
with these settings.
@alexcrichton
alexcrichton merged commit 7ce4604 into bytecodealliance:main Jun 18, 2021
@alexcrichton
alexcrichton deleted the pre-guard branch June 18, 2021 14:57
Comment thread tests/all/memory.rs
(2, &["i32.load16_s"]),
(4, &["i32.load" /*, "f32.load"*/]),
(8, &["i64.load" /*, "f64.load"*/]),
(16, &["v128.load"]),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This unconditionally adds a SIMD operation, which we don't support on s390x yet, causing the test to fail.

Can we make this part architecture-dependent for now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh sure yeah, it's also fine to just comment this out since it's not really the most critical part of the test anyway

@uweigand

Copy link
Copy Markdown
Member

Huh, it turns out this test case also exposes multiple pre-existing bugs in trap handling, both in the s390x back-end and in common code. All in all, I need the following four patches to make the test case pass:
#3012
#3013
#3014
#3015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fuzzing Issues related to our fuzzing infrastructure wasmtime:api Related to the API of the `wasmtime` crate itself

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants