Skip to content

Commit 980a136

Browse files
authored
Wasmtime: generalize async_stack_zeroing knob to cover initialization (bytecodealliance#10027)
* Wasmtime: generalize `async_stack_zeroing` knob to cover initialization This commit moves the knob from the `PoolingInstanceAllocatorConfig` to the regular `Config` and now controls both whether stacks are zeroed before reuse and whether they are zeroed before the initial use. The latter doesn't matter usually, since anonymous mmaps are already zeroed so we don't have to do anything there, but for no-std environments it is the difference between manually zeroing the stack or simply using unininitialized memory. * Fix CLI and test builds * fix default config value * fix some more tests
1 parent b5e044a commit 980a136

File tree

15 files changed

+137
-85
lines changed

15 files changed

+137
-85
lines changed

crates/c-api/include/wasmtime/async.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ typedef struct {
332332
* https://docs.wasmtime.dev/api/wasmtime/trait.StackCreator.html#tymethod.new_stack
333333
*/
334334
typedef wasmtime_error_t *(*wasmtime_new_stack_memory_callback_t)(
335-
void *env, size_t size, wasmtime_stack_memory_t *stack_ret);
335+
void *env, size_t size, bool zeroed, wasmtime_stack_memory_t *stack_ret);
336336

337337
/**
338338
* A representation of custom stack creator.

crates/c-api/src/async.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ unsafe impl StackMemory for CHostStackMemory {
397397
pub type wasmtime_new_stack_memory_callback_t = extern "C" fn(
398398
env: *mut std::ffi::c_void,
399399
size: usize,
400+
zeroed: bool,
400401
stack_ret: &mut wasmtime_stack_memory_t,
401402
) -> Option<Box<wasmtime_error_t>>;
402403

@@ -414,7 +415,7 @@ struct CHostStackCreator {
414415
unsafe impl Send for CHostStackCreator {}
415416
unsafe impl Sync for CHostStackCreator {}
416417
unsafe impl StackCreator for CHostStackCreator {
417-
fn new_stack(&self, size: usize) -> Result<Box<dyn wasmtime::StackMemory>> {
418+
fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn wasmtime::StackMemory>> {
418419
extern "C" fn panic_callback(_env: *mut std::ffi::c_void, _out_len: &mut usize) -> *mut u8 {
419420
panic!("a callback must be set");
420421
}
@@ -424,7 +425,7 @@ unsafe impl StackCreator for CHostStackCreator {
424425
finalizer: None,
425426
};
426427
let cb = self.new_stack;
427-
let result = cb(self.foreign.data, size, &mut out);
428+
let result = cb(self.foreign.data, size, zeroed, &mut out);
428429
match result {
429430
Some(error) => Err((*error).into()),
430431
None => Ok(Box::new(CHostStackMemory {

crates/cli-flags/src/lib.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,6 @@ wasmtime_option_group! {
131131
/// pooling allocator. (default: 100)
132132
pub pooling_max_unused_warm_slots: Option<u32>,
133133

134-
/// Configures whether or not stacks used for async futures are reset to
135-
/// zero after usage. (default: false)
136-
pub pooling_async_stack_zeroing: Option<bool>,
137-
138134
/// How much memory, in bytes, to keep resident for async stacks allocated
139135
/// with the pooling allocator. (default: 0)
140136
pub pooling_async_stack_keep_resident: Option<usize>,
@@ -276,6 +272,9 @@ wasmtime_option_group! {
276272
/// difference between the two is how much stack the host has to execute
277273
/// on.
278274
pub async_stack_size: Option<usize>,
275+
/// Configures whether or not stacks used for async futures are zeroed
276+
/// before (re)use as a defense-in-depth mechanism. (default: false)
277+
pub async_stack_zeroing: Option<bool>,
279278
/// Allow unknown exports when running commands.
280279
pub unknown_exports_allow: Option<bool>,
281280
/// Allow the main module to import unknown functions, using an
@@ -759,11 +758,6 @@ impl CommonOptions {
759758
if let Some(max) = self.opts.pooling_max_unused_warm_slots {
760759
cfg.max_unused_warm_slots(max);
761760
}
762-
match_feature! {
763-
["async" : self.opts.pooling_async_stack_zeroing]
764-
enable => cfg.async_stack_zeroing(enable),
765-
_ => err,
766-
}
767761
match_feature! {
768762
["async" : self.opts.pooling_async_stack_keep_resident]
769763
size => cfg.async_stack_keep_resident(size),
@@ -831,6 +825,11 @@ impl CommonOptions {
831825
size => config.async_stack_size(size),
832826
_ => err,
833827
}
828+
match_feature! {
829+
["async" : self.wasm.async_stack_zeroing]
830+
enable => config.async_stack_zeroing(enable),
831+
_ => err,
832+
}
834833

835834
if let Some(max) = self.wasm.max_wasm_stack {
836835
config.max_wasm_stack(max);

crates/fiber/src/lib.rs

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ pub type Result<T, E = imp::Error> = core::result::Result<T, E>;
4747

4848
impl FiberStack {
4949
/// Creates a new fiber stack of the given size.
50-
pub fn new(size: usize) -> Result<Self> {
51-
Ok(Self(imp::FiberStack::new(size)?))
50+
pub fn new(size: usize, zeroed: bool) -> Result<Self> {
51+
Ok(Self(imp::FiberStack::new(size, zeroed)?))
5252
}
5353

5454
/// Creates a new fiber stack of the given size.
@@ -108,7 +108,7 @@ pub unsafe trait RuntimeFiberStackCreator: Send + Sync {
108108
///
109109
/// This is useful to plugin previously allocated memory instead of mmap'ing a new stack for
110110
/// every instance.
111-
fn new_stack(&self, size: usize) -> Result<Box<dyn RuntimeFiberStack>, Error>;
111+
fn new_stack(&self, size: usize, zeroed: bool) -> Result<Box<dyn RuntimeFiberStack>, Error>;
112112
}
113113

114114
/// A fiber stack backed by custom memory.
@@ -276,11 +276,11 @@ mod tests {
276276

277277
#[test]
278278
fn small_stacks() {
279-
Fiber::<(), (), ()>::new(FiberStack::new(0).unwrap(), |_, _| {})
279+
Fiber::<(), (), ()>::new(FiberStack::new(0, false).unwrap(), |_, _| {})
280280
.unwrap()
281281
.resume(())
282282
.unwrap();
283-
Fiber::<(), (), ()>::new(FiberStack::new(1).unwrap(), |_, _| {})
283+
Fiber::<(), (), ()>::new(FiberStack::new(1, false).unwrap(), |_, _| {})
284284
.unwrap()
285285
.resume(())
286286
.unwrap();
@@ -290,10 +290,11 @@ mod tests {
290290
fn smoke() {
291291
let hit = Rc::new(Cell::new(false));
292292
let hit2 = hit.clone();
293-
let fiber = Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |_, _| {
294-
hit2.set(true);
295-
})
296-
.unwrap();
293+
let fiber =
294+
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024, false).unwrap(), move |_, _| {
295+
hit2.set(true);
296+
})
297+
.unwrap();
297298
assert!(!hit.get());
298299
fiber.resume(()).unwrap();
299300
assert!(hit.get());
@@ -303,12 +304,13 @@ mod tests {
303304
fn suspend_and_resume() {
304305
let hit = Rc::new(Cell::new(false));
305306
let hit2 = hit.clone();
306-
let fiber = Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |_, s| {
307-
s.suspend(());
308-
hit2.set(true);
309-
s.suspend(());
310-
})
311-
.unwrap();
307+
let fiber =
308+
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024, false).unwrap(), move |_, s| {
309+
s.suspend(());
310+
hit2.set(true);
311+
s.suspend(());
312+
})
313+
.unwrap();
312314
assert!(!hit.get());
313315
assert!(fiber.resume(()).is_err());
314316
assert!(!hit.get());
@@ -345,15 +347,17 @@ mod tests {
345347
}
346348

347349
fn run_test() {
348-
let fiber =
349-
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |(), s| {
350+
let fiber = Fiber::<(), (), ()>::new(
351+
FiberStack::new(1024 * 1024, false).unwrap(),
352+
move |(), s| {
350353
assert_contains_host();
351354
s.suspend(());
352355
assert_contains_host();
353356
s.suspend(());
354357
assert_contains_host();
355-
})
356-
.unwrap();
358+
},
359+
)
360+
.unwrap();
357361
assert!(fiber.resume(()).is_err());
358362
assert!(fiber.resume(()).is_err());
359363
assert!(fiber.resume(()).is_ok());
@@ -369,12 +373,14 @@ mod tests {
369373

370374
let a = Rc::new(Cell::new(false));
371375
let b = SetOnDrop(a.clone());
372-
let fiber =
373-
Fiber::<(), (), ()>::new(FiberStack::new(1024 * 1024).unwrap(), move |(), _s| {
376+
let fiber = Fiber::<(), (), ()>::new(
377+
FiberStack::new(1024 * 1024, false).unwrap(),
378+
move |(), _s| {
374379
let _ = &b;
375380
panic!();
376-
})
377-
.unwrap();
381+
},
382+
)
383+
.unwrap();
378384
assert!(panic::catch_unwind(AssertUnwindSafe(|| fiber.resume(()))).is_err());
379385
assert!(a.get());
380386

@@ -389,11 +395,14 @@ mod tests {
389395

390396
#[test]
391397
fn suspend_and_resume_values() {
392-
let fiber = Fiber::new(FiberStack::new(1024 * 1024).unwrap(), move |first, s| {
393-
assert_eq!(first, 2.0);
394-
assert_eq!(s.suspend(4), 3.0);
395-
"hello".to_string()
396-
})
398+
let fiber = Fiber::new(
399+
FiberStack::new(1024 * 1024, false).unwrap(),
400+
move |first, s| {
401+
assert_eq!(first, 2.0);
402+
assert_eq!(s.suspend(4), 3.0);
403+
"hello".to_string()
404+
},
405+
)
397406
.unwrap();
398407
assert_eq!(fiber.resume(2.0), Err(4));
399408
assert_eq!(fiber.resume(3.0), Ok("hello".to_string()));

crates/fiber/src/nostd.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,14 @@ fn align_ptr(ptr: *mut u8, len: usize, align: usize) -> (*mut u8, usize) {
6262
}
6363

6464
impl FiberStack {
65-
pub fn new(size: usize) -> Result<Self> {
65+
pub fn new(size: usize, zeroed: bool) -> Result<Self> {
6666
// Round up the size to at least one page.
6767
let size = core::cmp::max(4096, size);
68-
let mut storage = vec![0; size];
68+
let mut storage = Vec::new();
69+
storage.reserve_exact(size);
70+
if zeroed {
71+
storage.resize(size, 0);
72+
}
6973
let (base, len) = align_ptr(storage.as_mut_ptr(), size, STACK_ALIGN);
7074
Ok(FiberStack {
7175
storage,

crates/fiber/src/unix.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ enum FiberStackStorage {
6060
}
6161

6262
impl FiberStack {
63-
pub fn new(size: usize) -> io::Result<Self> {
63+
pub fn new(size: usize, zeroed: bool) -> io::Result<Self> {
64+
// The anonymous `mmap`s we use for `FiberStackStorage` are alawys
65+
// zeroed.
66+
let _ = zeroed;
67+
6468
// See comments in `mod asan` below for why asan has a different stack
6569
// allocation strategy.
6670
if cfg!(asan) {

crates/fiber/src/windows.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ pub type Error = io::Error;
1414
pub struct FiberStack(usize);
1515

1616
impl FiberStack {
17-
pub fn new(size: usize) -> io::Result<Self> {
17+
pub fn new(size: usize, zeroed: bool) -> io::Result<Self> {
18+
// We don't support fiber stack zeroing on windows.
19+
let _ = zeroed;
20+
1821
Ok(Self(size))
1922
}
2023

crates/fuzzing/src/generators/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ impl Config {
275275
))
276276
.allocation_strategy(self.wasmtime.strategy.to_wasmtime())
277277
.generate_address_map(self.wasmtime.generate_address_map)
278-
.signals_based_traps(self.wasmtime.signals_based_traps);
278+
.signals_based_traps(self.wasmtime.signals_based_traps)
279+
.async_stack_zeroing(self.wasmtime.async_stack_zeroing);
279280

280281
if !self.module_config.config.simd_enabled {
281282
cfg.wasm_relaxed_simd(false);
@@ -508,6 +509,7 @@ pub struct WasmtimeConfig {
508509
memory_init_cow: bool,
509510
memory_guaranteed_dense_image_size: u64,
510511
use_precompiled_cwasm: bool,
512+
async_stack_zeroing: bool,
511513
/// Configuration for the instance allocation strategy to use.
512514
pub strategy: InstanceAllocationStrategy,
513515
codegen: CodegenSettings,

crates/fuzzing/src/generators/pooling_config.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ pub struct PoolingAllocationConfig {
3030
pub decommit_batch_size: usize,
3131
pub max_unused_warm_slots: u32,
3232

33-
pub async_stack_zeroing: bool,
3433
pub async_stack_keep_resident: usize,
3534

3635
pub memory_protection_keys: MpkEnabled,
@@ -65,7 +64,6 @@ impl PoolingAllocationConfig {
6564
cfg.decommit_batch_size(self.decommit_batch_size);
6665
cfg.max_unused_warm_slots(self.max_unused_warm_slots);
6766

68-
cfg.async_stack_zeroing(self.async_stack_zeroing);
6967
cfg.async_stack_keep_resident(self.async_stack_keep_resident);
7068

7169
cfg.memory_protection_keys(self.memory_protection_keys);
@@ -112,7 +110,6 @@ impl<'a> Arbitrary<'a> for PoolingAllocationConfig {
112110
decommit_batch_size: u.int_in_range(1..=1000)?,
113111
max_unused_warm_slots: u.int_in_range(0..=total_memories + 10)?,
114112

115-
async_stack_zeroing: u.arbitrary()?,
116113
async_stack_keep_resident: u.int_in_range(0..=1 << 20)?,
117114

118115
memory_protection_keys: *u.choose(&[MpkEnabled::Auto, MpkEnabled::Disable])?,

0 commit comments

Comments
 (0)