Skip to content

Commit f820750

Browse files
authored
Fix double read/writes on stream/future handles (#12873)
* Fix double read/writes on stream/future handles This should result in a first-class trap, not a bug. * Fix CI
1 parent b860c2c commit f820750

6 files changed

Lines changed: 90 additions & 7 deletions

File tree

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ enum wasmtime_trap_code_enum {
131131
WASMTIME_TRAP_CODE_UNSUPPORTED_CALLBACK_CODE = 43,
132132
/// Cannot resume a thread which is not suspended.
133133
WASMTIME_TRAP_CODE_CANNOT_RESUME_THREAD = 44,
134+
/// Cannot issue a read/write on a future/stream while there is a
135+
/// pending operation already.
136+
WASMTIME_TRAP_CODE_CONCURRENT_FUTURE_STREAM_OP = 45,
134137
};
135138

136139
/**

crates/c-api/src/trap.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const _: () = {
5151
assert!(Trap::BackpressureOverflow as u8 == 42);
5252
assert!(Trap::UnsupportedCallbackCode as u8 == 43);
5353
assert!(Trap::CannotResumeThread as u8 == 44);
54+
assert!(Trap::ConcurrentFutureStreamOp as u8 == 45);
5455
};
5556

5657
#[repr(C)]

crates/environ/src/trap_encoding.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,10 @@ generate_trap_type! {
216216
/// Cannot resume a thread which is not suspended.
217217
CannotResumeThread = "cannot resume thread which is not suspended",
218218

219+
/// Cannot issue a read/write on a future/stream while there is a
220+
/// pending operation already.
221+
ConcurrentFutureStreamOp = "cannot have concurrent operations active on a future/stream",
222+
219223
// if adding a variant here be sure to update `trap.rs` and `trap.h` as
220224
// mentioned above
221225
}

crates/wasmtime/src/runtime/component/concurrent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,6 @@ impl Status {
142142
#[derive(Clone, Copy, Debug)]
143143
enum Event {
144144
None,
145-
Cancelled,
146145
Subtask {
147146
status: Status,
148147
},
@@ -162,6 +161,7 @@ enum Event {
162161
code: ReturnCode,
163162
pending: Option<(TypeFutureTableIndex, u32)>,
164163
},
164+
Cancelled,
165165
}
166166

167167
impl Event {

crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::vm::component::{ComponentInstance, HandleTable, TransmitLocalState};
1414
use crate::vm::{AlwaysMut, VMStore};
1515
use crate::{AsContext, AsContextMut, StoreContextMut, ValRaw};
1616
use crate::{
17-
Error, Result, bail, bail_bug, ensure,
17+
Error, Result, Trap, bail, bail_bug, ensure,
1818
error::{Context as _, format_err},
1919
};
2020
use buffers::{Extender, SliceBuffer, UntypedWriteBuffer};
@@ -3423,10 +3423,7 @@ impl Instance {
34233423
self.check_bounds(store.0, options, ty, address, count)?;
34243424
let (rep, state) = self.id().get_mut(store.0).get_mut_by_index(ty, handle)?;
34253425
let TransmitLocalState::Write { done } = *state else {
3426-
bail!(
3427-
"invalid handle {handle}; expected `Write`; got {:?}",
3428-
*state
3429-
);
3426+
bail!(Trap::ConcurrentFutureStreamOp);
34303427
};
34313428

34323429
if done {
@@ -3668,7 +3665,7 @@ impl Instance {
36683665
self.check_bounds(store.0, options, ty, address, count)?;
36693666
let (rep, state) = self.id().get_mut(store.0).get_mut_by_index(ty, handle)?;
36703667
let TransmitLocalState::Read { done } = *state else {
3671-
bail_bug!("invalid handle {handle}; expected `Read`; got {:?}", *state);
3668+
bail!(Trap::ConcurrentFutureStreamOp);
36723669
};
36733670

36743671
if done {

tests/misc_testsuite/component-model/async/streams.wast

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,81 @@
108108
(core func $stream-drop-writable (canon stream.drop-writable $stream-type))
109109
(core instance $i (instantiate $m (with "" (instance (export "stream.drop-writable" (func $stream-drop-writable))))))
110110
)
111+
112+
;; Test which exercises an intra-component stream read/write to ensure that
113+
;; there's no Miri violations while doing this.
114+
(component definition $A
115+
(core module $libc (memory (export "mem") 1))
116+
(core instance $libc (instantiate $libc))
117+
(core module $ics
118+
(import "" "stream.new" (func $stream.new (result i64)))
119+
(import "" "stream.read" (func $stream.read (param i32 i32 i32) (result i32)))
120+
(import "" "stream.write" (func $stream.write (param i32 i32 i32) (result i32)))
121+
(import "" "mem" (memory 1))
122+
123+
(global $r (mut i32) (i32.const 0))
124+
(global $w (mut i32) (i32.const 0))
125+
126+
(func (export "read-twice")
127+
call $init
128+
129+
(call $stream.read
130+
(global.get $r)
131+
(i32.const 100)
132+
(i32.const 100))
133+
i32.const -1 ;; BLOCKED
134+
i32.ne
135+
if unreachable end
136+
137+
(call $stream.read
138+
(global.get $r)
139+
(i32.const 0)
140+
(i32.const 0))
141+
unreachable
142+
)
143+
144+
(func (export "write-twice")
145+
call $init
146+
147+
(call $stream.write
148+
(global.get $w)
149+
(i32.const 100)
150+
(i32.const 100))
151+
i32.const -1 ;; BLOCKED
152+
i32.ne
153+
if unreachable end
154+
155+
(call $stream.write
156+
(global.get $w)
157+
(i32.const 0)
158+
(i32.const 0))
159+
unreachable
160+
)
161+
162+
(func $init
163+
(local $t64 i64)
164+
165+
(local.set $t64 (call $stream.new))
166+
(global.set $r (i32.wrap_i64 (local.get $t64)))
167+
(global.set $w (i32.wrap_i64 (i64.shr_u (local.get $t64) (i64.const 32))))
168+
)
169+
)
170+
(type $s (stream u8))
171+
(core func $stream.new (canon stream.new $s))
172+
(core func $stream.read (canon stream.read $s async (memory $libc "mem")))
173+
(core func $stream.write (canon stream.write $s async (memory $libc "mem")))
174+
(core instance $ics (instantiate $ics
175+
(with "" (instance
176+
(export "stream.new" (func $stream.new))
177+
(export "stream.read" (func $stream.read))
178+
(export "stream.write" (func $stream.write))
179+
(export "mem" (memory $libc "mem"))
180+
))
181+
))
182+
(func (export "read-twice") async (canon lift (core func $ics "read-twice")))
183+
(func (export "write-twice") async (canon lift (core func $ics "write-twice")))
184+
)
185+
(component instance $A $A)
186+
(assert_trap (invoke "read-twice") "cannot have concurrent operations active on a future/stream")
187+
(component instance $A $A)
188+
(assert_trap (invoke "write-twice") "cannot have concurrent operations active on a future/stream")

0 commit comments

Comments
 (0)