Skip to content

Commit f4b58a7

Browse files
authored
Fix panic in atomic_wait{32,64} with massive timeouts (#13038)
This fixes a panic due to the `Instant` deadline not being representable when the timeout duration is `Duration::MAX`. The solution here is to use `checked_add` and pretend there's no timeout on overflow since that's waiting long enough to effectively have no timeout. This is not reachable from WebAssembly as it can't represent long enough durations but is reachable via the embedder API. Additionally shared memories are gated by default, so this doesn't affect any default configuration.
1 parent 2b31135 commit f4b58a7

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

crates/wasmtime/src/runtime/vm/memory/shared_memory.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,12 @@ impl SharedMemory {
138138
assert!(std::mem::size_of::<AtomicU32>() == 4);
139139
assert!(std::mem::align_of::<AtomicU32>() <= 4);
140140
let atomic = unsafe { AtomicU32::from_ptr(addr.cast()) };
141-
let deadline = timeout.map(|d| Instant::now() + d);
141+
142+
// Note that `checked_add` is used such that when `timeout` is too large
143+
// it'll cause there to be no timeout at all if we can't represent the
144+
// deadline. That effectively maps to the requested timeout since if we
145+
// can't represent the deadline we'll be here awhile.
146+
let deadline = timeout.and_then(|d| Instant::now().checked_add(d));
142147

143148
WAITER.with(|waiter| {
144149
let mut waiter = waiter.borrow_mut();
@@ -162,7 +167,9 @@ impl SharedMemory {
162167
assert!(std::mem::size_of::<AtomicU64>() == 8);
163168
assert!(std::mem::align_of::<AtomicU64>() <= 8);
164169
let atomic = unsafe { AtomicU64::from_ptr(addr.cast()) };
165-
let deadline = timeout.map(|d| Instant::now() + d);
170+
171+
// See `atomic_wait32` for why this is using `checked_add`.
172+
let deadline = timeout.and_then(|d| Instant::now().checked_add(d));
166173

167174
WAITER.with(|waiter| {
168175
let mut waiter = waiter.borrow_mut();

tests/all/memory.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use rayon::prelude::*;
2-
use std::sync::atomic::{AtomicU32, Ordering::SeqCst};
2+
use std::sync::Arc;
3+
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering::SeqCst};
4+
use std::thread;
35
use std::time::Duration;
46
use wasmtime::*;
57
use wasmtime_test_macros::wasmtime_test;
@@ -777,3 +779,39 @@ fn configure_zero(config: &mut Config) -> Result<()> {
777779

778780
Ok(())
779781
}
782+
783+
#[test]
784+
fn atomic_wait_massive_timeout() -> Result<()> {
785+
let mut config = Config::new();
786+
config.shared_memory(true);
787+
let engine = Engine::new(&config)?;
788+
789+
let memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?;
790+
let result = memory.atomic_wait32(0, 1, Some(Duration::MAX));
791+
assert_eq!(result, Ok(WaitResult::Mismatch));
792+
let result = memory.atomic_wait64(0, 1, Some(Duration::MAX));
793+
assert_eq!(result, Ok(WaitResult::Mismatch));
794+
795+
let done = Arc::new(AtomicBool::new(false));
796+
let t = thread::spawn({
797+
let memory = memory.clone();
798+
let done = done.clone();
799+
move || {
800+
while !done.load(SeqCst) {
801+
memory.atomic_notify(0, 1).unwrap();
802+
memory.atomic_notify(8, 1).unwrap();
803+
}
804+
}
805+
});
806+
807+
let result = memory.atomic_wait32(0, 0, Some(Duration::MAX));
808+
assert_eq!(result, Ok(WaitResult::Ok));
809+
let result = memory.atomic_wait64(8, 0, Some(Duration::MAX));
810+
assert_eq!(result, Ok(WaitResult::Ok));
811+
812+
done.store(true, SeqCst);
813+
814+
t.join().unwrap();
815+
816+
Ok(())
817+
}

0 commit comments

Comments
 (0)