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,461 @@
1
+ //! kobako's own answers to the 36 preview1 imports that read the
2
+ //! descriptor table: `fd_*`, `path_*`, `sock_*`, and `poll_oneoff`.
3
+ //!
4
+ //! wasmtime-wasi keeps its table private, so a descriptor kobako places
5
+ //! there would stay invisible to its calls. Answering every call that reads
6
+ //! a descriptor keeps the guest on one table, `crate::descriptors`, where
7
+ //! each Mount is a preopen. The other ten imports — arguments,
8
+ //! environment, clocks, process, scheduling, and randomness — stay
9
+ //! wasmtime-wasi's.
10
+
11
+ mod args;
12
+ mod beneath;
13
+ mod errno;
14
+ mod fd;
15
+ mod memory;
16
+ mod path;
17
+ mod poll;
18
+ mod sock;
19
+
20
+ #[cfg(test)]
21
+ mod differential;
22
+ #[cfg(test)]
23
+ mod mount_view;
24
+
25
+ use wasmtime::{Caller, Extern, Linker};
26
+
27
+ use crate::descriptors::Descriptors;
28
+ use crate::invocation::Invocation;
29
+ use errno::{Answer, Failure};
30
+ use memory::GuestMemory;
31
+ use path::OpenFlags;
32
+
33
+ const MODULE: &str = "wasi_snapshot_preview1";
34
+
35
+ /// Run one call against the calling instance's memory and invocation,
36
+ /// answering its `errno`, or the trap that ends the invocation.
37
+ fn answer(
38
+ caller: &mut Caller<'_, Invocation>,
39
+ call: impl FnOnce(&mut GuestMemory, &mut Invocation) -> Answer,
40
+ ) -> wasmtime::Result<i32> {
41
+ let memory = caller
42
+ .get_export("memory")
43
+ .and_then(Extern::into_memory)
44
+ .ok_or_else(|| wasmtime::Error::msg("a preview1 call needs the guest's `memory` export"))?;
45
+ let (bytes, invocation) = memory.data_and_store_mut(caller);
46
+ let answer = call(&mut GuestMemory::new(bytes), invocation);
47
+ if let Some(trap) = invocation.abandonment() {
48
+ return Err(trap);
49
+ }
50
+ match answer {
51
+ Ok(()) => Ok(0),
52
+ Err(Failure::Errno(errno)) => Ok(errno as i32),
53
+ Err(Failure::Trap(trap)) => Err(trap),
54
+ }
55
+ }
56
+
57
+ fn table(invocation: &mut Invocation) -> &mut Descriptors {
58
+ invocation.wasi_mut().descriptors_mut()
59
+ }
60
+
61
+ /// Define kobako's answers over wasmtime-wasi's for the same names.
62
+ pub(crate) fn shadow(linker: &mut Linker<Invocation>) -> wasmtime::Result<()> {
63
+ linker.allow_shadowing(true);
64
+ shadow_fd(linker)?;
65
+ shadow_path(linker)?;
66
+ shadow_poll_and_sock(linker)?;
67
+ linker.allow_shadowing(false);
68
+ Ok(())
69
+ }
70
+
71
+ fn shadow_fd(linker: &mut Linker<Invocation>) -> wasmtime::Result<()> {
72
+ type C<'a> = Caller<'a, Invocation>;
73
+ linker.func_wrap(
74
+ MODULE,
75
+ "fd_advise",
76
+ |mut c: C, fd: i32, offset: i64, len: i64, advice: i32| {
77
+ answer(&mut c, |_, inv| {
78
+ fd::advise(
79
+ table(inv),
80
+ fd as u32,
81
+ offset as u64,
82
+ len as u64,
83
+ advice as u32,
84
+ )
85
+ })
86
+ },
87
+ )?;
88
+ linker.func_wrap(
89
+ MODULE,
90
+ "fd_allocate",
91
+ |mut c: C, fd: i32, _: i64, _: i64| {
92
+ answer(&mut c, |_, inv| fd::allocate(table(inv), fd as u32))
93
+ },
94
+ )?;
95
+ linker.func_wrap(MODULE, "fd_close", |mut c: C, fd: i32| {
96
+ answer(&mut c, |_, inv| fd::close(table(inv), fd as u32))
97
+ })?;
98
+ // A Filesystem makes what was written durable in one step, so data
99
+ // and metadata sync alike.
100
+ for name in ["fd_datasync", "fd_sync"] {
101
+ linker.func_wrap(MODULE, name, |mut c: C, fd: i32| {
102
+ answer(&mut c, |_, inv| fd::sync(table(inv), fd as u32))
103
+ })?;
104
+ }
105
+ linker.func_wrap(MODULE, "fd_fdstat_get", |mut c: C, fd: i32, out: i32| {
106
+ answer(&mut c, |mem, inv| {
107
+ fd::fdstat_get(table(inv), mem, fd as u32, out as u32)
108
+ })
109
+ })?;
110
+ linker.func_wrap(
111
+ MODULE,
112
+ "fd_fdstat_set_flags",
113
+ |mut c: C, fd: i32, flags: i32| {
114
+ answer(&mut c, |_, inv| {
115
+ fd::fdstat_set_flags(table(inv), fd as u32, flags as u32)
116
+ })
117
+ },
118
+ )?;
119
+ linker.func_wrap(
120
+ MODULE,
121
+ "fd_fdstat_set_rights",
122
+ |mut c: C, fd: i32, base: i64, inheriting: i64| {
123
+ answer(&mut c, |_, inv| {
124
+ fd::fdstat_set_rights(table(inv), fd as u32, base as u64, inheriting as u64)
125
+ })
126
+ },
127
+ )?;
128
+ linker.func_wrap(MODULE, "fd_filestat_get", |mut c: C, fd: i32, out: i32| {
129
+ answer(&mut c, |mem, inv| {
130
+ fd::filestat_get(table(inv), mem, fd as u32, out as u32)
131
+ })
132
+ })?;
133
+ linker.func_wrap(
134
+ MODULE,
135
+ "fd_filestat_set_size",
136
+ |mut c: C, fd: i32, size: i64| {
137
+ answer(&mut c, |_, inv| {
138
+ fd::filestat_set_size(table(inv), fd as u32, size as u64)
139
+ })
140
+ },
141
+ )?;
142
+ linker.func_wrap(
143
+ MODULE,
144
+ "fd_filestat_set_times",
145
+ |mut c: C, fd: i32, _: i64, _: i64, fst_flags: i32| {
146
+ answer(&mut c, |_, inv| {
147
+ fd::filestat_set_times(table(inv), fd as u32, fst_flags as u32)
148
+ })
149
+ },
150
+ )?;
151
+ linker.func_wrap(
152
+ MODULE,
153
+ "fd_pread",
154
+ |mut c: C, fd: i32, iovs: i32, iovs_len: i32, offset: i64, nread: i32| {
155
+ answer(&mut c, |mem, inv| {
156
+ let iovs = (iovs as u32, iovs_len as u32);
157
+ fd::pread(
158
+ table(inv),
159
+ mem,
160
+ fd as u32,
161
+ iovs,
162
+ offset as u64,
163
+ nread as u32,
164
+ )
165
+ })
166
+ },
167
+ )?;
168
+ linker.func_wrap(MODULE, "fd_prestat_get", |mut c: C, fd: i32, out: i32| {
169
+ answer(&mut c, |mem, inv| {
170
+ fd::prestat_get(table(inv), mem, fd as u32, out as u32)
171
+ })
172
+ })?;
173
+ linker.func_wrap(
174
+ MODULE,
175
+ "fd_prestat_dir_name",
176
+ |mut c: C, fd: i32, path: i32, path_len: i32| {
177
+ answer(&mut c, |mem, inv| {
178
+ fd::prestat_dir_name(table(inv), mem, fd as u32, path as u32, path_len as u32)
179
+ })
180
+ },
181
+ )?;
182
+ linker.func_wrap(
183
+ MODULE,
184
+ "fd_pwrite",
185
+ |mut c: C, fd: i32, ciovs: i32, ciovs_len: i32, offset: i64, nwritten: i32| {
186
+ answer(&mut c, |mem, inv| {
187
+ let ciovs = (ciovs as u32, ciovs_len as u32);
188
+ fd::pwrite(
189
+ table(inv),
190
+ mem,
191
+ fd as u32,
192
+ ciovs,
193
+ offset as u64,
194
+ nwritten as u32,
195
+ )
196
+ })
197
+ },
198
+ )?;
199
+ linker.func_wrap(
200
+ MODULE,
201
+ "fd_read",
202
+ |mut c: C, fd: i32, iovs: i32, iovs_len: i32, nread: i32| {
203
+ answer(&mut c, |mem, inv| {
204
+ let iovs = (iovs as u32, iovs_len as u32);
205
+ fd::read(table(inv), mem, fd as u32, iovs, nread as u32)
206
+ })
207
+ },
208
+ )?;
209
+ linker.func_wrap(
210
+ MODULE,
211
+ "fd_readdir",
212
+ |mut c: C, fd: i32, buf: i32, buf_len: i32, cookie: i64, bufused: i32| {
213
+ answer(&mut c, |mem, inv| {
214
+ let buf = (buf as u32, buf_len as u32);
215
+ fd::readdir(
216
+ table(inv),
217
+ mem,
218
+ fd as u32,
219
+ buf,
220
+ cookie as u64,
221
+ bufused as u32,
222
+ )
223
+ })
224
+ },
225
+ )?;
226
+ linker.func_wrap(MODULE, "fd_renumber", |mut c: C, from: i32, to: i32| {
227
+ answer(&mut c, |_, inv| {
228
+ fd::renumber(table(inv), from as u32, to as u32)
229
+ })
230
+ })?;
231
+ linker.func_wrap(
232
+ MODULE,
233
+ "fd_seek",
234
+ |mut c: C, fd: i32, offset: i64, whence: i32, out: i32| {
235
+ answer(&mut c, |mem, inv| {
236
+ fd::seek(
237
+ table(inv),
238
+ mem,
239
+ fd as u32,
240
+ offset,
241
+ whence as u32,
242
+ out as u32,
243
+ )
244
+ })
245
+ },
246
+ )?;
247
+ linker.func_wrap(MODULE, "fd_tell", |mut c: C, fd: i32, out: i32| {
248
+ answer(&mut c, |mem, inv| {
249
+ fd::tell(table(inv), mem, fd as u32, out as u32)
250
+ })
251
+ })?;
252
+ linker.func_wrap(
253
+ MODULE,
254
+ "fd_write",
255
+ |mut c: C, fd: i32, ciovs: i32, ciovs_len: i32, nwritten: i32| {
256
+ answer(&mut c, |mem, inv| {
257
+ let ciovs = (ciovs as u32, ciovs_len as u32);
258
+ fd::write(table(inv), mem, fd as u32, ciovs, nwritten as u32)
259
+ })
260
+ },
261
+ )?;
262
+ Ok(())
263
+ }
264
+
265
+ fn shadow_path(linker: &mut Linker<Invocation>) -> wasmtime::Result<()> {
266
+ type C<'a> = Caller<'a, Invocation>;
267
+ linker.func_wrap(
268
+ MODULE,
269
+ "path_create_directory",
270
+ |mut c: C, fd: i32, path: i32, len: i32| {
271
+ answer(&mut c, |mem, inv| {
272
+ path::create_directory(table(inv), mem, fd as u32, (path as u32, len as u32))
273
+ })
274
+ },
275
+ )?;
276
+ linker.func_wrap(
277
+ MODULE,
278
+ "path_filestat_get",
279
+ |mut c: C, fd: i32, flags: i32, path: i32, len: i32, out: i32| {
280
+ answer(&mut c, |mem, inv| {
281
+ let path = (path as u32, len as u32);
282
+ path::filestat_get(table(inv), mem, fd as u32, flags as u32, path, out as u32)
283
+ })
284
+ },
285
+ )?;
286
+ linker.func_wrap(
287
+ MODULE,
288
+ "path_filestat_set_times",
289
+ |mut c: C, fd: i32, flags: i32, path: i32, len: i32, _: i64, _: i64, fst_flags: i32| {
290
+ answer(&mut c, |mem, inv| {
291
+ let path = (path as u32, len as u32);
292
+ path::filestat_set_times(
293
+ table(inv),
294
+ mem,
295
+ fd as u32,
296
+ flags as u32,
297
+ path,
298
+ fst_flags as u32,
299
+ )
300
+ })
301
+ },
302
+ )?;
303
+ linker.func_wrap(
304
+ MODULE,
305
+ "path_link",
306
+ |mut c: C,
307
+ old_fd: i32,
308
+ old_flags: i32,
309
+ old_path: i32,
310
+ old_len: i32,
311
+ new_fd: i32,
312
+ new_path: i32,
313
+ new_len: i32| {
314
+ answer(&mut c, |mem, inv| {
315
+ path::link(
316
+ table(inv),
317
+ mem,
318
+ old_fd as u32,
319
+ old_flags as u32,
320
+ (old_path as u32, old_len as u32),
321
+ new_fd as u32,
322
+ (new_path as u32, new_len as u32),
323
+ )
324
+ })
325
+ },
326
+ )?;
327
+ linker.func_wrap(
328
+ MODULE,
329
+ "path_open",
330
+ |mut c: C,
331
+ fd: i32,
332
+ dirflags: i32,
333
+ path: i32,
334
+ path_len: i32,
335
+ oflags: i32,
336
+ rights_base: i64,
337
+ rights_inheriting: i64,
338
+ fdflags: i32,
339
+ out: i32| {
340
+ let flags = OpenFlags {
341
+ dirflags: dirflags as u32,
342
+ oflags: oflags as u32,
343
+ rights_base: rights_base as u64,
344
+ rights_inheriting: rights_inheriting as u64,
345
+ fdflags: fdflags as u32,
346
+ };
347
+ answer(&mut c, |mem, inv| {
348
+ let path = (path as u32, path_len as u32);
349
+ path::open(table(inv), mem, fd as u32, path, &flags, out as u32)
350
+ })
351
+ },
352
+ )?;
353
+ linker.func_wrap(
354
+ MODULE,
355
+ "path_readlink",
356
+ |mut c: C, fd: i32, path: i32, len: i32, _: i32, _: i32, _: i32| {
357
+ answer(&mut c, |mem, inv| {
358
+ path::readlink(table(inv), mem, fd as u32, (path as u32, len as u32))
359
+ })
360
+ },
361
+ )?;
362
+ linker.func_wrap(
363
+ MODULE,
364
+ "path_remove_directory",
365
+ |mut c: C, fd: i32, path: i32, len: i32| {
366
+ answer(&mut c, |mem, inv| {
367
+ path::remove_directory(table(inv), mem, fd as u32, (path as u32, len as u32))
368
+ })
369
+ },
370
+ )?;
371
+ linker.func_wrap(
372
+ MODULE,
373
+ "path_rename",
374
+ |mut c: C, fd: i32, old: i32, old_len: i32, new_fd: i32, new: i32, new_len: i32| {
375
+ answer(&mut c, |mem, inv| {
376
+ path::rename(
377
+ table(inv),
378
+ mem,
379
+ (fd as u32, (old as u32, old_len as u32)),
380
+ (new_fd as u32, (new as u32, new_len as u32)),
381
+ )
382
+ })
383
+ },
384
+ )?;
385
+ linker.func_wrap(
386
+ MODULE,
387
+ "path_symlink",
388
+ |mut c: C, old: i32, old_len: i32, fd: i32, new: i32, new_len: i32| {
389
+ answer(&mut c, |mem, inv| {
390
+ path::symlink(
391
+ table(inv),
392
+ mem,
393
+ (old as u32, old_len as u32),
394
+ fd as u32,
395
+ (new as u32, new_len as u32),
396
+ )
397
+ })
398
+ },
399
+ )?;
400
+ linker.func_wrap(
401
+ MODULE,
402
+ "path_unlink_file",
403
+ |mut c: C, fd: i32, path: i32, len: i32| {
404
+ answer(&mut c, |mem, inv| {
405
+ path::unlink_file(table(inv), mem, fd as u32, (path as u32, len as u32))
406
+ })
407
+ },
408
+ )?;
409
+ Ok(())
410
+ }
411
+
412
+ fn shadow_poll_and_sock(linker: &mut Linker<Invocation>) -> wasmtime::Result<()> {
413
+ type C<'a> = Caller<'a, Invocation>;
414
+ linker.func_wrap(
415
+ MODULE,
416
+ "poll_oneoff",
417
+ |mut c: C, subs: i32, events: i32, count: i32, nevents: i32| {
418
+ answer(&mut c, |mem, inv| {
419
+ poll::poll_oneoff(
420
+ inv,
421
+ mem,
422
+ subs as u32,
423
+ events as u32,
424
+ count as u32,
425
+ nevents as u32,
426
+ )
427
+ })
428
+ },
429
+ )?;
430
+ linker.func_wrap(
431
+ MODULE,
432
+ "sock_accept",
433
+ |mut c: C, fd: i32, flags: i32, _: i32| {
434
+ answer(&mut c, |_, inv| {
435
+ sock::accept(table(inv), fd as u32, flags as u32)
436
+ })
437
+ },
438
+ )?;
439
+ linker.func_wrap(
440
+ MODULE,
441
+ "sock_recv",
442
+ |mut c: C, fd: i32, _: i32, _: i32, ri_flags: i32, _: i32, _: i32| {
443
+ answer(&mut c, |_, inv| {
444
+ sock::recv(table(inv), fd as u32, ri_flags as u32)
445
+ })
446
+ },
447
+ )?;
448
+ linker.func_wrap(
449
+ MODULE,
450
+ "sock_send",
451
+ |mut c: C, fd: i32, _: i32, _: i32, _: i32, _: i32| {
452
+ answer(&mut c, |_, inv| sock::send(table(inv), fd as u32))
453
+ },
454
+ )?;
455
+ linker.func_wrap(MODULE, "sock_shutdown", |mut c: C, fd: i32, how: i32| {
456
+ answer(&mut c, |_, inv| {
457
+ sock::shutdown(table(inv), fd as u32, how as u32)
458
+ })
459
+ })?;
460
+ Ok(())
461
+ }
@@ -7,9 +7,9 @@
7
7
  //! pure function over the error's downcast chain so it can be exercised
8
8
  //! from `cargo test` without any frontend.
9
9
 
10
- use std::time::Instant;
10
+ use std::time::{Duration, Instant};
11
11
 
12
- use wasmtime::{StoreContextMut, UpdateDeadline};
12
+ use wasmtime::{Store, StoreContextMut, UpdateDeadline};
13
13
 
14
14
  use crate::invocation::Invocation;
15
15
  use crate::limiter::MemoryLimitTrap;
@@ -53,13 +53,33 @@ pub(crate) fn epoch_deadline_callback(
53
53
  /// The message leaves out the ABI export symbol, so the Sandbox layer can
54
54
  /// attach the caller's verb instead. A cap trap carries its own message,
55
55
  /// since wasmtime's backtrace framing is noise there.
56
- pub(crate) fn trap_from(err: wasmtime::Error) -> Trap {
56
+ pub(crate) fn to_trap(err: wasmtime::Error) -> Trap {
57
+ cap_trap(&err).unwrap_or_else(|| Trap::Other(other_trap_message(&err)))
58
+ }
59
+
60
+ /// Arm a Store's wall-clock deadline `limit` from now, or none. Epoch
61
+ /// interruption is on engine-wide and a fresh Store's deadline has already
62
+ /// elapsed, so an unbounded Store names one it cannot reach.
63
+ pub(crate) fn arm_deadline(store: &mut Store<Invocation>, limit: Option<Duration>) {
64
+ store.epoch_deadline_callback(epoch_deadline_callback);
65
+ store
66
+ .data_mut()
67
+ .set_deadline(limit.map(|limit| Instant::now() + limit));
68
+ store.set_epoch_deadline(if limit.is_some() {
69
+ 1
70
+ } else {
71
+ NO_TIMEOUT_EPOCH_DELTA
72
+ });
73
+ }
74
+
75
+ /// The cap trap `err` carries, if a cap fired: a setup step that fails
76
+ /// for any other reason words its own message instead.
77
+ pub(crate) fn cap_trap(err: &wasmtime::Error) -> Option<Trap> {
57
78
  if let Some(t) = err.downcast_ref::<TimeoutTrap>() {
58
- Trap::Timeout(t.to_string())
59
- } else if let Some(t) = err.downcast_ref::<MemoryLimitTrap>() {
60
- Trap::MemoryLimit(t.to_string())
79
+ Some(Trap::Timeout(t.to_string()))
61
80
  } else {
62
- Trap::Other(other_trap_message(&err))
81
+ err.downcast_ref::<MemoryLimitTrap>()
82
+ .map(|t| Trap::MemoryLimit(t.to_string()))
63
83
  }
64
84
  }
65
85
 
@@ -76,17 +96,17 @@ fn other_trap_message(err: &wasmtime::Error) -> String {
76
96
  }
77
97
  }
78
98
 
79
- /// The template is built before any invocation, so neither cap can fire:
80
- /// the memory cap is not yet armed and the probe Store's epoch deadline is
81
- /// out of reach. Every failure here is a setup fault, not a trap.
99
+ /// The template is built before any invocation, so no invocation cap can
100
+ /// fire: the memory cap is not yet armed, and the probe's own setup
101
+ /// deadline is told apart where it fires. Every failure here is a setup
102
+ /// fault, not a trap.
82
103
  pub(crate) fn instantiate_err(err: wasmtime::Error) -> SetupError {
83
104
  SetupError::Dead(format!("instantiate: {err}"))
84
105
  }
85
106
 
86
107
  #[cfg(test)]
87
108
  mod tests {
88
- use super::{other_trap_message, trap_from, TimeoutTrap, NO_TIMEOUT_EPOCH_DELTA};
89
- use crate::invocation::Invocation;
109
+ use super::{other_trap_message, to_trap, TimeoutTrap, NO_TIMEOUT_EPOCH_DELTA};
90
110
  use crate::limiter::MemoryLimitTrap;
91
111
  use kobako_runtime::error::Trap;
92
112
 
@@ -102,32 +122,32 @@ mod tests {
102
122
  fn no_timeout_delta_survives_a_ticked_engine_epoch() {
103
123
  let engine = crate::cache::shared_engine().expect("shared engine must be constructible");
104
124
  engine.increment_epoch();
105
- let mut store = wasmtime::Store::new(engine, Invocation::new(None));
125
+ let mut store = wasmtime::Store::new(engine, ());
106
126
  store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
107
127
  }
108
128
 
109
129
  // @behavior OC-032
110
130
  #[test]
111
- fn trap_from_routes_timeout_trap_to_timeout() {
131
+ fn to_trap_routes_timeout_trap_to_timeout() {
112
132
  let err = wasmtime::Error::new(TimeoutTrap);
113
133
  let expected = TimeoutTrap.to_string();
114
- assert!(matches!(trap_from(err), Trap::Timeout(msg) if msg == expected));
134
+ assert!(matches!(to_trap(err), Trap::Timeout(msg) if msg == expected));
115
135
  }
116
136
 
117
137
  // @behavior OC-033
118
138
  #[test]
119
- fn trap_from_routes_memory_limit_trap_to_memory_limit() {
139
+ fn to_trap_routes_memory_limit_trap_to_memory_limit() {
120
140
  let trap = MemoryLimitTrap::new(1 << 20, 1 << 19);
121
141
  let expected = trap.to_string();
122
142
  let err = wasmtime::Error::new(trap);
123
- assert!(matches!(trap_from(err), Trap::MemoryLimit(msg) if msg == expected));
143
+ assert!(matches!(to_trap(err), Trap::MemoryLimit(msg) if msg == expected));
124
144
  }
125
145
 
126
146
  // @behavior OC-028
127
147
  #[test]
128
- fn trap_from_falls_back_to_other_for_unknown_errors() {
148
+ fn to_trap_falls_back_to_other_for_unknown_errors() {
129
149
  let err = wasmtime::Error::msg("some other wasmtime fault");
130
- assert!(matches!(trap_from(err), Trap::Other(_)));
150
+ assert!(matches!(to_trap(err), Trap::Other(_)));
131
151
  }
132
152
 
133
153
  // A guest hard trap reaches the host as a wasmtime error whose Display is