kobako 0.28.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. checksums.yaml +4 -4
  2. data/.release-please-manifest.json +1 -1
  3. data/CHANGELOG.md +170 -0
  4. data/Cargo.lock +111 -82
  5. data/README.md +154 -36
  6. data/ROADMAP.md +5 -0
  7. data/SECURITY.md +1 -1
  8. data/crates/kobako-runtime/CHANGELOG.md +86 -0
  9. data/crates/kobako-runtime/Cargo.toml +27 -6
  10. data/crates/kobako-runtime/README.md +13 -8
  11. data/crates/kobako-runtime/src/dispatch.rs +8 -0
  12. data/crates/kobako-runtime/src/filesystem/directory/spelling.rs +194 -0
  13. data/crates/kobako-runtime/src/filesystem/directory.rs +1082 -0
  14. data/crates/kobako-runtime/src/filesystem/memory.rs +1100 -0
  15. data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +88 -0
  16. data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +808 -0
  17. data/crates/kobako-runtime/src/filesystem/overlay.rs +1955 -0
  18. data/crates/kobako-runtime/src/filesystem/path.rs +60 -0
  19. data/crates/kobako-runtime/src/filesystem/temporary.rs +167 -0
  20. data/crates/kobako-runtime/src/filesystem.rs +392 -0
  21. data/crates/kobako-runtime/src/grant.rs +20 -0
  22. data/crates/kobako-runtime/src/lib.rs +11 -1
  23. data/crates/kobako-runtime/src/mount.rs +178 -0
  24. data/crates/kobako-runtime/src/profile.rs +4 -4
  25. data/crates/kobako-runtime/src/runtime.rs +12 -0
  26. data/crates/kobako-runtime/src/snapshot.rs +20 -5
  27. data/crates/kobako-runtime/src/yielder.rs +11 -6
  28. data/crates/kobako-transport/CHANGELOG.md +17 -0
  29. data/crates/kobako-transport/Cargo.toml +1 -1
  30. data/crates/kobako-transport/README.md +1 -1
  31. data/crates/kobako-transport/src/abi.rs +1 -1
  32. data/crates/kobako-transport/src/envelope/fault.rs +2 -1
  33. data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
  34. data/crates/kobako-transport/src/envelope/reply.rs +32 -14
  35. data/crates/kobako-transport/src/envelope/request.rs +18 -8
  36. data/crates/kobako-wasmtime/CHANGELOG.md +119 -0
  37. data/crates/kobako-wasmtime/Cargo.toml +21 -16
  38. data/crates/kobako-wasmtime/README.md +5 -3
  39. data/crates/kobako-wasmtime/src/abi.rs +66 -32
  40. data/crates/kobako-wasmtime/src/ambient.rs +63 -4
  41. data/crates/kobako-wasmtime/src/cache.rs +185 -74
  42. data/crates/kobako-wasmtime/src/config.rs +58 -1
  43. data/crates/kobako-wasmtime/src/descriptors.rs +167 -0
  44. data/crates/kobako-wasmtime/src/dispatch.rs +47 -29
  45. data/crates/kobako-wasmtime/src/driver.rs +46 -44
  46. data/crates/kobako-wasmtime/src/guest_mem.rs +13 -7
  47. data/crates/kobako-wasmtime/src/imports.rs +46 -0
  48. data/crates/kobako-wasmtime/src/instance_pre.rs +17 -45
  49. data/crates/kobako-wasmtime/src/invocation.rs +35 -46
  50. data/crates/kobako-wasmtime/src/io.rs +5 -202
  51. data/crates/kobako-wasmtime/src/lib.rs +4 -0
  52. data/crates/kobako-wasmtime/src/preview1/args.rs +60 -0
  53. data/crates/kobako-wasmtime/src/preview1/beneath.rs +188 -0
  54. data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +609 -0
  55. data/crates/kobako-wasmtime/src/preview1/differential.rs +372 -0
  56. data/crates/kobako-wasmtime/src/preview1/errno.rs +267 -0
  57. data/crates/kobako-wasmtime/src/preview1/fd.rs +672 -0
  58. data/crates/kobako-wasmtime/src/preview1/memory.rs +205 -0
  59. data/crates/kobako-wasmtime/src/preview1/mount_view.rs +2499 -0
  60. data/crates/kobako-wasmtime/src/preview1/path.rs +421 -0
  61. data/crates/kobako-wasmtime/src/preview1/poll.rs +333 -0
  62. data/crates/kobako-wasmtime/src/preview1/sock.rs +30 -0
  63. data/crates/kobako-wasmtime/src/preview1.rs +461 -0
  64. data/crates/kobako-wasmtime/src/trap.rs +39 -19
  65. data/crates/kobako-wasmtime/src/wasi.rs +317 -0
  66. data/crates/kobako-wasmtime/tests/compile_posture.rs +43 -0
  67. data/crates/kobako-wasmtime/tests/guest_file.rs +1054 -0
  68. data/crates/kobako-wasmtime/tests/preamble_flags.rs +3 -2
  69. data/crates/kobako-wasmtime/tests/snippet_compile.rs +4 -3
  70. data/crates/kobako-wasmtime/tests/support/mod.rs +9 -13
  71. data/crates/kobako-wasmtime/tests/unanswered_call.rs +86 -0
  72. data/crates/kobako-wasmtime/tests/unsupported_call.rs +1 -0
  73. data/data/kobako.wasm +0 -0
  74. data/ext/kobako/Cargo.toml +4 -2
  75. data/ext/kobako/src/runtime/bridge.rs +154 -81
  76. data/ext/kobako/src/runtime/escape.rs +53 -0
  77. data/ext/kobako/src/runtime/filesystem/grants.rs +91 -0
  78. data/ext/kobako/src/runtime/filesystem/ruby.rs +400 -0
  79. data/ext/kobako/src/runtime/filesystem.rs +236 -0
  80. data/ext/kobako/src/runtime/gvl.rs +7 -1
  81. data/ext/kobako/src/runtime/pinned_value.rs +31 -0
  82. data/ext/kobako/src/runtime/snapshot.rs +127 -0
  83. data/ext/kobako/src/runtime.rs +120 -199
  84. data/lib/kobako/catalog/flags.rb +2 -14
  85. data/lib/kobako/catalog/grants.rb +30 -0
  86. data/lib/kobako/catalog/mount.rb +27 -0
  87. data/lib/kobako/catalog/services.rb +11 -0
  88. data/lib/kobako/catalog/snippets.rb +5 -9
  89. data/lib/kobako/catalog.rb +11 -0
  90. data/lib/kobako/codec/decoder.rb +1 -0
  91. data/lib/kobako/codec/ext_types.rb +1 -1
  92. data/lib/kobako/codec/state.rb +19 -3
  93. data/lib/kobako/codec/value_budget.rb +86 -0
  94. data/lib/kobako/codec.rb +7 -0
  95. data/lib/kobako/context.rb +38 -11
  96. data/lib/kobako/errors.rb +15 -14
  97. data/lib/kobako/filesystem.rb +38 -0
  98. data/lib/kobako/pool.rb +2 -2
  99. data/lib/kobako/sandbox.rb +40 -12
  100. data/lib/kobako/sandbox_options.rb +39 -12
  101. data/lib/kobako/snippet/{binary.rb → bytecode.rb} +1 -1
  102. data/lib/kobako/snippet.rb +1 -1
  103. data/lib/kobako/transport/block_failure.rb +35 -0
  104. data/lib/kobako/transport/dispatcher.rb +30 -26
  105. data/lib/kobako/transport/exposure.rb +5 -7
  106. data/lib/kobako/transport/reflection.rb +79 -23
  107. data/lib/kobako/transport/yielder.rb +8 -30
  108. data/lib/kobako/transport.rb +1 -0
  109. data/lib/kobako/version.rb +1 -1
  110. data/lib/kobako.rb +1 -0
  111. data/sig/kobako/catalog/flags.rbs +0 -4
  112. data/sig/kobako/catalog/grants.rbs +13 -0
  113. data/sig/kobako/catalog/mount.rbs +28 -0
  114. data/sig/kobako/catalog/services.rbs +4 -0
  115. data/sig/kobako/catalog/snippets.rbs +1 -1
  116. data/sig/kobako/catalog.rbs +1 -0
  117. data/sig/kobako/codec/state.rbs +7 -1
  118. data/sig/kobako/codec/value_budget.rbs +23 -0
  119. data/sig/kobako/codec.rbs +2 -0
  120. data/sig/kobako/context.rbs +14 -6
  121. data/sig/kobako/errors.rbs +4 -4
  122. data/sig/kobako/filesystem.rbs +88 -0
  123. data/sig/kobako/guest_yielder.rbs +9 -0
  124. data/sig/kobako/pool.rbs +1 -1
  125. data/sig/kobako/runtime.rbs +21 -49
  126. data/sig/kobako/sandbox.rbs +7 -1
  127. data/sig/kobako/sandbox_options.rbs +24 -2
  128. data/sig/kobako/snapshot.rbs +20 -0
  129. data/sig/kobako/snippet/bytecode.rbs +12 -0
  130. data/sig/kobako/transport/block_failure.rbs +13 -0
  131. data/sig/kobako/transport/dispatcher.rbs +3 -3
  132. data/sig/kobako/transport/exposure.rbs +1 -1
  133. data/sig/kobako/transport/reflection.rbs +14 -1
  134. data/sig/kobako/transport/yielder.rbs +3 -7
  135. data/sig/kobako/transport.rbs +2 -2
  136. metadata +60 -7
  137. data/sig/kobako/snippet/binary.rbs +0 -12
@@ -0,0 +1,333 @@
1
+ //! `poll_oneoff`, the guest's wait: for a clock, or for a descriptor to be
2
+ //! ready.
3
+ //!
4
+ //! Every descriptor kobako holds is ready at once, so only a clock ever
5
+ //! waits. A wait passes on the guest's own clocks: the hermetic rung's
6
+ //! never advance, so its waits end at once, and a permissive wait takes
7
+ //! host time but never past the invocation's deadline — the host is the
8
+ //! one waiting, where no epoch check can interrupt it.
9
+
10
+ use std::thread;
11
+ use std::time::{Duration, Instant};
12
+
13
+ use super::errno::{Answer, Errno, Failure};
14
+ use super::memory::GuestMemory;
15
+ use crate::descriptors::Descriptor;
16
+ use crate::invocation::Invocation;
17
+ use crate::trap::TimeoutTrap;
18
+
19
+ const SUBSCRIPTION_SIZE: u32 = 48;
20
+ const EVENT_SIZE: u32 = 32;
21
+
22
+ const TAG_CLOCK: u8 = 0;
23
+ const TAG_FD_READ: u8 = 1;
24
+ const TAG_FD_WRITE: u8 = 2;
25
+
26
+ const CLOCK_REALTIME: u32 = 0;
27
+ const CLOCK_MONOTONIC: u32 = 1;
28
+ /// The highest `clockid` variant.
29
+ const CLOCK_MAX: u32 = 3;
30
+ const SUBCLOCKFLAGS_ABSTIME: u16 = 1;
31
+
32
+ /// One subscription as the guest wrote it.
33
+ struct Subscription {
34
+ userdata: u64,
35
+ kind: Kind,
36
+ }
37
+
38
+ enum Kind {
39
+ /// A clock, as the nanoseconds left before it fires.
40
+ Clock(u64),
41
+ /// A descriptor to read; `hangup` when a file has nothing left past
42
+ /// its position.
43
+ FdRead {
44
+ hangup: bool,
45
+ },
46
+ FdWrite,
47
+ }
48
+
49
+ const EVENTRWFLAGS_HANGUP: u16 = 1;
50
+
51
+ pub(crate) fn poll_oneoff(
52
+ invocation: &Invocation,
53
+ mem: &mut GuestMemory,
54
+ subs: u32,
55
+ events: u32,
56
+ count: u32,
57
+ nevents: u32,
58
+ ) -> Answer {
59
+ if count == 0 {
60
+ return Err(Errno::Inval.into());
61
+ }
62
+ mem.consume(count, SUBSCRIPTION_SIZE)?;
63
+ mem.consume(count, EVENT_SIZE)?;
64
+ let subscriptions = (0..count)
65
+ .map(|index| {
66
+ let sub = GuestMemory::element(subs, index, SUBSCRIPTION_SIZE)?;
67
+ read_subscription(invocation, mem, sub)
68
+ })
69
+ .collect::<Answer<Vec<_>>>()?;
70
+
71
+ let clocks = subscriptions.iter().filter_map(|sub| match sub.kind {
72
+ Kind::Clock(left) => Some(left),
73
+ Kind::FdRead { .. } | Kind::FdWrite => None,
74
+ });
75
+ let waited = if clocks.clone().count() == subscriptions.len() {
76
+ let soonest = clocks.min().unwrap_or(0);
77
+ wait(invocation, soonest)?;
78
+ soonest
79
+ } else {
80
+ 0
81
+ };
82
+
83
+ let mut written = 0;
84
+ for sub in &subscriptions {
85
+ let (ready, nbytes) = match sub.kind {
86
+ Kind::Clock(left) => (left <= waited, 0),
87
+ Kind::FdRead { .. } | Kind::FdWrite => (true, 1),
88
+ };
89
+ if ready {
90
+ let event = GuestMemory::element(events, written, EVENT_SIZE)?;
91
+ write_event(mem, event, sub, nbytes)?;
92
+ written += 1;
93
+ }
94
+ }
95
+ mem.write_u32(nevents, written)
96
+ }
97
+
98
+ fn read_subscription(invocation: &Invocation, mem: &GuestMemory, sub: u32) -> Answer<Subscription> {
99
+ let userdata = mem.read_u64(sub)?;
100
+ let kind = match mem.read_u8(GuestMemory::field(sub, 8)?)? {
101
+ TAG_CLOCK => read_clock(invocation, mem, sub)?,
102
+ TAG_FD_READ => match descriptor(invocation, mem, sub)? {
103
+ Descriptor::Stdin(_) => Kind::FdRead { hangup: false },
104
+ Descriptor::File(file) => Kind::FdRead {
105
+ hangup: file.file.metadata()?.len <= file.position,
106
+ },
107
+ Descriptor::Channel(_) | Descriptor::Directory(_) => return Err(Errno::Badf.into()),
108
+ },
109
+ TAG_FD_WRITE => match descriptor(invocation, mem, sub)? {
110
+ Descriptor::Channel(_) => Kind::FdWrite,
111
+ Descriptor::File(file) if file.mount.allows_writes() => Kind::FdWrite,
112
+ Descriptor::File(_) => return Err(Errno::Perm.into()),
113
+ Descriptor::Stdin(_) | Descriptor::Directory(_) => return Err(Errno::Badf.into()),
114
+ },
115
+ _ => return Err(Errno::Inval.into()),
116
+ };
117
+ Ok(Subscription { userdata, kind })
118
+ }
119
+
120
+ fn descriptor<'a>(
121
+ invocation: &'a Invocation,
122
+ mem: &GuestMemory,
123
+ sub: u32,
124
+ ) -> Answer<&'a Descriptor> {
125
+ let fd = mem.read_u32(GuestMemory::field(sub, 16)?)?;
126
+ invocation
127
+ .wasi()
128
+ .descriptors()
129
+ .get(fd)
130
+ .ok_or_else(|| Errno::Badf.into())
131
+ }
132
+
133
+ /// A clock, read against the guest's own clocks. Only the realtime and
134
+ /// monotonic clocks can be waited on, alone or among other
135
+ /// subscriptions, as in wasmtime-wasi.
136
+ fn read_clock(invocation: &Invocation, mem: &GuestMemory, sub: u32) -> Answer<Kind> {
137
+ let id = mem.read_u32(GuestMemory::field(sub, 16)?)?;
138
+ let timeout = mem.read_u64(GuestMemory::field(sub, 24)?)?;
139
+ let flags = mem.read_u16(GuestMemory::field(sub, 40)?)?;
140
+ if id > CLOCK_MAX || flags & !SUBCLOCKFLAGS_ABSTIME != 0 {
141
+ return Err(Errno::Inval.into());
142
+ }
143
+ let absolute = flags & SUBCLOCKFLAGS_ABSTIME != 0;
144
+ let clocks = invocation.wasi().clocks();
145
+ let now = match (id, absolute) {
146
+ (CLOCK_REALTIME | CLOCK_MONOTONIC, false) => 0,
147
+ (CLOCK_MONOTONIC, true) => clocks.monotonic_now(),
148
+ (CLOCK_REALTIME, true) => clocks.wall_now(),
149
+ _ => return Err(Errno::Inval.into()),
150
+ };
151
+ Ok(Kind::Clock(timeout.saturating_sub(now)))
152
+ }
153
+
154
+ /// Wait `nanos` of the guest's time: none on frozen clocks, and on live
155
+ /// ones no further than the invocation's deadline, which ends it as a
156
+ /// timeout.
157
+ fn wait(invocation: &Invocation, nanos: u64) -> Answer {
158
+ if nanos == 0 || !invocation.wasi().clocks().advances() {
159
+ return Ok(());
160
+ }
161
+ let wanted = Duration::from_nanos(nanos);
162
+ if let Some(deadline) = invocation.deadline() {
163
+ let left = deadline.saturating_duration_since(Instant::now());
164
+ if wanted >= left {
165
+ thread::sleep(left);
166
+ return Err(Failure::Trap(wasmtime::Error::new(TimeoutTrap)));
167
+ }
168
+ }
169
+ thread::sleep(wanted);
170
+ Ok(())
171
+ }
172
+
173
+ fn write_event(mem: &mut GuestMemory, event: u32, sub: &Subscription, nbytes: u64) -> Answer {
174
+ let (kind, flags) = match sub.kind {
175
+ Kind::Clock(_) => (TAG_CLOCK, 0),
176
+ Kind::FdRead { hangup } => (TAG_FD_READ, if hangup { EVENTRWFLAGS_HANGUP } else { 0 }),
177
+ Kind::FdWrite => (TAG_FD_WRITE, 0),
178
+ };
179
+ mem.write_u64(event, sub.userdata)?;
180
+ mem.write_u16(GuestMemory::field(event, 8)?, 0)?;
181
+ mem.write_u8(GuestMemory::field(event, 10)?, kind)?;
182
+ mem.write_u64(GuestMemory::field(event, 16)?, nbytes)?;
183
+ mem.write_u16(GuestMemory::field(event, 24)?, flags)
184
+ }
185
+
186
+ #[cfg(test)]
187
+ mod tests {
188
+ //! Witness that a guest's clock wait passes on its posture's clocks:
189
+ //! at once on the frozen ones, and on the live ones no further than
190
+ //! the deadline.
191
+ use std::time::{Duration, Instant};
192
+
193
+ use wasmtime::{Module, Store};
194
+
195
+ use crate::cache::shared_engine;
196
+ use crate::config::Config;
197
+ use crate::imports;
198
+ use crate::invocation::Invocation;
199
+ use crate::trap::{to_trap, NO_TIMEOUT_EPOCH_DELTA};
200
+ use crate::wasi::Wasi;
201
+ use kobako_runtime::error::Trap;
202
+ use kobako_runtime::profile::Profile;
203
+
204
+ /// One relative monotonic-clock subscription of ten seconds at 0,
205
+ /// its event written at 64 and the event count at 96.
206
+ const WAIT_WAT: &str = r#"
207
+ (module
208
+ (import "wasi_snapshot_preview1" "poll_oneoff"
209
+ (func $poll (param i32 i32 i32 i32) (result i32)))
210
+ (memory (export "memory") 1)
211
+ (func (export "wait_ten_seconds") (result i32)
212
+ (i32.store8 (i32.const 8) (i32.const 0))
213
+ (i32.store (i32.const 16) (i32.const 1))
214
+ (i64.store (i32.const 24) (i64.const 10000000000))
215
+ (call $poll (i32.const 0) (i32.const 64) (i32.const 1) (i32.const 96))))
216
+ "#;
217
+
218
+ /// Run the ten-second wait at `profile` under `deadline`, answering
219
+ /// how it ended and how long the host spent on it.
220
+ fn wait_ten_seconds(
221
+ profile: Profile,
222
+ deadline: Option<Duration>,
223
+ ) -> (wasmtime::Result<i32>, Duration) {
224
+ let engine = shared_engine().expect("shared engine must be constructible");
225
+ let config = Config {
226
+ timeout: None,
227
+ setup_timeout: None,
228
+ memory_limit: None,
229
+ stdout_limit: None,
230
+ stderr_limit: None,
231
+ open_file_limit: None,
232
+ profile,
233
+ };
234
+ let wasi = Wasi::for_invocation(&config, &[], &[]).expect("WASI context must build");
235
+ let mut store = Store::new(engine, Invocation::new(None, wasi));
236
+ store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
237
+ let linker = imports::linker().expect("host imports must link");
238
+ let module = Module::new(engine, WAIT_WAT).expect("wait module must compile");
239
+ let instance = linker
240
+ .instantiate(&mut store, &module)
241
+ .expect("wait module must instantiate");
242
+ let wait = instance
243
+ .get_typed_func::<(), i32>(&mut store, "wait_ten_seconds")
244
+ .expect("wait export must resolve");
245
+ let started = Instant::now();
246
+ store
247
+ .data_mut()
248
+ .set_deadline(deadline.map(|left| started + left));
249
+ let ended = wait.call(&mut store, ());
250
+ (ended, started.elapsed())
251
+ }
252
+
253
+ // @behavior RT-073
254
+ #[test]
255
+ fn a_hermetic_wait_ends_without_the_host_waiting() {
256
+ let (ended, spent) = wait_ten_seconds(Profile::Hermetic, None);
257
+ assert_eq!(
258
+ ended.ok(),
259
+ Some(0),
260
+ "a ten-second clock wait through a hermetic guest's poll_oneoff must succeed"
261
+ );
262
+ assert!(
263
+ spent < Duration::from_secs(1),
264
+ "a ten-second clock wait through a hermetic guest's poll_oneoff must return at once, \
265
+ not after {spent:?}"
266
+ );
267
+ }
268
+
269
+ /// What a lone relative wait of no time on clock `id` answers.
270
+ fn wait_alone_on(id: i32) -> i32 {
271
+ const WAIT_ON_WAT: &str = r#"
272
+ (module
273
+ (import "wasi_snapshot_preview1" "poll_oneoff"
274
+ (func $poll (param i32 i32 i32 i32) (result i32)))
275
+ (memory (export "memory") 1)
276
+ (func (export "wait_on") (param $id i32) (result i32)
277
+ (i32.store (i32.const 16) (local.get $id))
278
+ (call $poll (i32.const 0) (i32.const 64) (i32.const 1) (i32.const 96))))
279
+ "#;
280
+ let engine = shared_engine().expect("shared engine must be constructible");
281
+ let config = Config {
282
+ timeout: None,
283
+ setup_timeout: None,
284
+ memory_limit: None,
285
+ stdout_limit: None,
286
+ stderr_limit: None,
287
+ open_file_limit: None,
288
+ profile: Profile::Hermetic,
289
+ };
290
+ let wasi = Wasi::for_invocation(&config, &[], &[]).expect("WASI context must build");
291
+ let mut store = Store::new(engine, Invocation::new(None, wasi));
292
+ store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
293
+ let linker = imports::linker().expect("host imports must link");
294
+ let module = Module::new(engine, WAIT_ON_WAT).expect("wait module must compile");
295
+ linker
296
+ .instantiate(&mut store, &module)
297
+ .expect("wait module must instantiate")
298
+ .get_typed_func::<i32, i32>(&mut store, "wait_on")
299
+ .expect("wait export must resolve")
300
+ .call(&mut store, id)
301
+ .expect("the wait must not trap")
302
+ }
303
+
304
+ // @behavior RT-131
305
+ #[test]
306
+ fn a_lone_wait_on_a_cpu_time_clock_is_invalid() {
307
+ assert_eq!(
308
+ [0, 1, 2, 3].map(wait_alone_on),
309
+ [0, 0, ERRNO_INVAL, ERRNO_INVAL],
310
+ "a lone relative wait through poll_oneoff on the process or thread CPU-time clock \
311
+ must answer EINVAL as wasmtime-wasi does, while the realtime and monotonic clocks wait"
312
+ );
313
+ }
314
+
315
+ const ERRNO_INVAL: i32 = 28;
316
+
317
+ // @behavior RT-074
318
+ #[test]
319
+ fn a_permissive_wait_ends_at_the_deadline_as_a_timeout() {
320
+ let (ended, spent) = wait_ten_seconds(Profile::Permissive, Some(Duration::from_millis(50)));
321
+ let trap = ended.expect_err("a wait past the deadline must not return to the guest");
322
+ assert!(
323
+ matches!(to_trap(trap), Trap::Timeout(_)),
324
+ "a clock wait past the deadline through a permissive guest's poll_oneoff must end \
325
+ as a timeout"
326
+ );
327
+ assert!(
328
+ spent >= Duration::from_millis(50) && spent < Duration::from_secs(5),
329
+ "a clock wait past the deadline through a permissive guest's poll_oneoff must end \
330
+ at the deadline, not after {spent:?}"
331
+ );
332
+ }
333
+ }
@@ -0,0 +1,30 @@
1
+ //! The 4 `sock_*` calls. The table holds no socket, so every open
2
+ //! descriptor answers `NOTSOCK`, as wasmtime-wasi's preview1 does.
3
+
4
+ use super::args;
5
+ use super::errno::{Answer, Errno};
6
+ use crate::descriptors::Descriptors;
7
+
8
+ fn socket(table: &Descriptors, fd: u32) -> Answer {
9
+ table.get(fd).ok_or(Errno::Badf)?;
10
+ Err(Errno::Notsock.into())
11
+ }
12
+
13
+ pub(crate) fn accept(table: &Descriptors, fd: u32, flags: u32) -> Answer {
14
+ args::flags(flags, args::FDFLAGS)?;
15
+ socket(table, fd)
16
+ }
17
+
18
+ pub(crate) fn recv(table: &Descriptors, fd: u32, ri_flags: u32) -> Answer {
19
+ args::flags(ri_flags, args::RIFLAGS)?;
20
+ socket(table, fd)
21
+ }
22
+
23
+ pub(crate) fn send(table: &Descriptors, fd: u32) -> Answer {
24
+ socket(table, fd)
25
+ }
26
+
27
+ pub(crate) fn shutdown(table: &Descriptors, fd: u32, how: u32) -> Answer {
28
+ args::flags(how, args::SDFLAGS)?;
29
+ socket(table, fd)
30
+ }