kobako 0.27.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.
- checksums.yaml +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +241 -0
- data/Cargo.lock +132 -126
- data/README.md +240 -147
- data/ROADMAP.md +28 -22
- data/SECURITY.md +2 -2
- data/crates/kobako-runtime/CHANGELOG.md +117 -0
- data/crates/kobako-runtime/Cargo.toml +27 -6
- data/crates/kobako-runtime/README.md +16 -10
- data/crates/kobako-runtime/src/dispatch.rs +8 -0
- data/crates/kobako-runtime/src/error.rs +8 -9
- data/crates/kobako-runtime/src/filesystem/directory/spelling.rs +194 -0
- data/crates/kobako-runtime/src/filesystem/directory.rs +1082 -0
- data/crates/kobako-runtime/src/filesystem/memory.rs +1100 -0
- data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +88 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +808 -0
- data/crates/kobako-runtime/src/filesystem/overlay.rs +1955 -0
- data/crates/kobako-runtime/src/filesystem/path.rs +60 -0
- data/crates/kobako-runtime/src/filesystem/temporary.rs +167 -0
- data/crates/kobako-runtime/src/filesystem.rs +392 -0
- data/crates/kobako-runtime/src/grant.rs +20 -0
- data/crates/kobako-runtime/src/lib.rs +12 -2
- data/crates/kobako-runtime/src/mount.rs +178 -0
- data/crates/kobako-runtime/src/profile.rs +4 -4
- data/crates/kobako-runtime/src/runtime.rs +43 -17
- data/crates/kobako-runtime/src/snapshot.rs +33 -15
- data/crates/kobako-runtime/src/yielder.rs +14 -6
- data/crates/kobako-transport/CHANGELOG.md +45 -0
- data/crates/kobako-transport/Cargo.toml +1 -1
- data/crates/kobako-transport/README.md +21 -12
- data/crates/kobako-transport/src/abi.rs +11 -14
- data/crates/kobako-transport/src/envelope/bytes.rs +18 -6
- data/crates/kobako-transport/src/envelope/compile_reply.rs +139 -0
- data/crates/kobako-transport/src/envelope/fault.rs +2 -1
- data/crates/kobako-transport/src/envelope/outcome.rs +17 -16
- data/crates/kobako-transport/src/envelope/preamble.rs +513 -0
- data/crates/kobako-transport/src/envelope/reply.rs +103 -61
- data/crates/kobako-transport/src/envelope/request.rs +305 -0
- data/crates/kobako-transport/src/envelope/run.rs +1 -2
- data/crates/kobako-transport/src/envelope/tag.rs +49 -0
- data/crates/kobako-transport/src/envelope/unsupported.rs +105 -0
- data/crates/kobako-transport/src/envelope.rs +19 -12
- data/crates/kobako-transport/src/lib.rs +0 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +163 -0
- data/crates/kobako-wasmtime/Cargo.toml +23 -16
- data/crates/kobako-wasmtime/README.md +6 -4
- data/crates/kobako-wasmtime/src/abi.rs +67 -36
- data/crates/kobako-wasmtime/src/ambient.rs +64 -5
- data/crates/kobako-wasmtime/src/cache.rs +204 -127
- data/crates/kobako-wasmtime/src/capture.rs +4 -11
- data/crates/kobako-wasmtime/src/config.rs +69 -15
- data/crates/kobako-wasmtime/src/descriptors.rs +167 -0
- data/crates/kobako-wasmtime/src/dispatch.rs +95 -87
- data/crates/kobako-wasmtime/src/driver.rs +174 -125
- data/crates/kobako-wasmtime/src/exports.rs +88 -34
- data/crates/kobako-wasmtime/src/guest_mem.rs +151 -138
- data/crates/kobako-wasmtime/src/imports.rs +46 -0
- data/crates/kobako-wasmtime/src/instance_pre.rs +19 -58
- data/crates/kobako-wasmtime/src/invocation.rs +84 -412
- data/crates/kobako-wasmtime/src/io.rs +89 -0
- data/crates/kobako-wasmtime/src/lib.rs +7 -24
- data/crates/kobako-wasmtime/src/limiter.rs +253 -0
- data/crates/kobako-wasmtime/src/preview1/args.rs +60 -0
- data/crates/kobako-wasmtime/src/preview1/beneath.rs +188 -0
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +609 -0
- data/crates/kobako-wasmtime/src/preview1/differential.rs +372 -0
- data/crates/kobako-wasmtime/src/preview1/errno.rs +267 -0
- data/crates/kobako-wasmtime/src/preview1/fd.rs +672 -0
- data/crates/kobako-wasmtime/src/preview1/memory.rs +205 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +2499 -0
- data/crates/kobako-wasmtime/src/preview1/path.rs +421 -0
- data/crates/kobako-wasmtime/src/preview1/poll.rs +333 -0
- data/crates/kobako-wasmtime/src/preview1/sock.rs +30 -0
- data/crates/kobako-wasmtime/src/preview1.rs +461 -0
- data/crates/kobako-wasmtime/src/trap.rs +66 -59
- data/crates/kobako-wasmtime/src/wasi.rs +317 -0
- data/crates/kobako-wasmtime/tests/compile_posture.rs +43 -0
- data/crates/kobako-wasmtime/tests/guest_file.rs +1054 -0
- data/crates/kobako-wasmtime/tests/preamble_flags.rs +70 -0
- data/crates/kobako-wasmtime/tests/snippet_compile.rs +95 -0
- data/crates/kobako-wasmtime/tests/support/mod.rs +30 -0
- data/crates/kobako-wasmtime/tests/unanswered_call.rs +86 -0
- data/crates/kobako-wasmtime/tests/unsupported_call.rs +51 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +4 -2
- data/ext/kobako/src/runtime/bridge.rs +177 -113
- data/ext/kobako/src/runtime/errors.rs +26 -32
- data/ext/kobako/src/runtime/escape.rs +53 -0
- data/ext/kobako/src/runtime/filesystem/grants.rs +91 -0
- data/ext/kobako/src/runtime/filesystem/ruby.rs +400 -0
- data/ext/kobako/src/runtime/filesystem.rs +236 -0
- data/ext/kobako/src/runtime/gvl.rs +11 -13
- data/ext/kobako/src/runtime/pinned_value.rs +31 -0
- data/ext/kobako/src/runtime/snapshot.rs +127 -0
- data/ext/kobako/src/runtime.rs +190 -312
- data/lib/kobako/capture.rb +1 -4
- data/lib/kobako/catalog/extensions.rb +21 -59
- data/lib/kobako/catalog/flags.rb +29 -0
- data/lib/kobako/catalog/grants.rb +30 -0
- data/lib/kobako/catalog/handles.rb +7 -54
- data/lib/kobako/catalog/mount.rb +27 -0
- data/lib/kobako/catalog/services.rb +17 -60
- data/lib/kobako/catalog/snippets.rb +41 -69
- data/lib/kobako/catalog.rb +13 -10
- data/lib/kobako/codec/decoder.rb +10 -28
- data/lib/kobako/codec/encoder.rb +3 -22
- data/lib/kobako/codec/error.rb +6 -27
- data/lib/kobako/codec/ext_types.rb +72 -94
- data/lib/kobako/codec/handle_walk.rb +65 -130
- data/lib/kobako/codec/nesting.rb +3 -8
- data/lib/kobako/codec/state.rb +21 -11
- data/lib/kobako/codec/utils.rb +6 -34
- data/lib/kobako/codec/value_budget.rb +86 -0
- data/lib/kobako/codec.rb +10 -22
- data/lib/kobako/constant_name.rb +8 -0
- data/lib/kobako/context.rb +83 -117
- data/lib/kobako/errors.rb +57 -132
- data/lib/kobako/execution.rb +16 -21
- data/lib/kobako/extension.rb +23 -40
- data/lib/kobako/filesystem.rb +38 -0
- data/lib/kobako/handle.rb +7 -12
- data/lib/kobako/outcome.rb +51 -80
- data/lib/kobako/payload/arguments.rb +1 -16
- data/lib/kobako/payload.rb +4 -12
- data/lib/kobako/pool.rb +25 -55
- data/lib/kobako/runtime.rb +7 -21
- data/lib/kobako/sandbox.rb +128 -137
- data/lib/kobako/sandbox_options.rb +71 -90
- data/lib/kobako/snippet/bytecode.rb +11 -0
- data/lib/kobako/snippet/source.rb +1 -16
- data/lib/kobako/snippet.rb +2 -10
- data/lib/kobako/transport/block_failure.rb +35 -0
- data/lib/kobako/transport/call.rb +1 -14
- data/lib/kobako/transport/dispatcher.rb +127 -206
- data/lib/kobako/transport/error.rb +6 -20
- data/lib/kobako/transport/exposure.rb +10 -17
- data/lib/kobako/transport/reflection.rb +108 -63
- data/lib/kobako/transport/run.rb +17 -71
- data/lib/kobako/transport/yielder.rb +20 -94
- data/lib/kobako/transport.rb +4 -15
- data/lib/kobako/unresolved.rb +4 -11
- data/lib/kobako/usage.rb +2 -6
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +1 -0
- data/sig/kobako/catalog/extensions.rbs +2 -2
- data/sig/kobako/catalog/flags.rbs +13 -0
- data/sig/kobako/catalog/grants.rbs +13 -0
- data/sig/kobako/catalog/mount.rbs +28 -0
- data/sig/kobako/catalog/services.rbs +3 -5
- data/sig/kobako/catalog/snippets.rbs +5 -5
- data/sig/kobako/catalog.rbs +1 -0
- data/sig/kobako/codec/ext_types.rbs +10 -8
- data/sig/kobako/codec/handle_walk.rbs +9 -7
- data/sig/kobako/codec/state.rbs +7 -1
- data/sig/kobako/codec/utils.rbs +2 -2
- data/sig/kobako/codec/value_budget.rbs +23 -0
- data/sig/kobako/codec.rbs +4 -2
- data/sig/kobako/constant_name.rbs +3 -0
- data/sig/kobako/context.rbs +15 -8
- data/sig/kobako/errors.rbs +10 -4
- data/sig/kobako/filesystem.rbs +88 -0
- data/sig/kobako/guest_yielder.rbs +9 -0
- data/sig/kobako/outcome.rbs +6 -8
- data/sig/kobako/pool.rbs +1 -1
- data/sig/kobako/runtime.rbs +21 -45
- data/sig/kobako/sandbox.rbs +11 -1
- data/sig/kobako/sandbox_options.rbs +25 -7
- data/sig/kobako/snapshot.rbs +20 -0
- data/sig/kobako/snippet/bytecode.rbs +12 -0
- data/sig/kobako/transport/block_failure.rbs +13 -0
- data/sig/kobako/transport/dispatcher.rbs +15 -19
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +19 -4
- data/sig/kobako/transport/run.rbs +1 -3
- data/sig/kobako/transport/yielder.rbs +5 -9
- data/sig/kobako/transport.rbs +9 -8
- metadata +75 -10
- data/crates/kobako-transport/src/envelope/invocation_frames.rs +0 -241
- data/crates/kobako-wasmtime/src/frames.rs +0 -273
- data/lib/kobako/snippet/binary.rb +0 -26
- data/rust-toolchain.toml +0 -15
- 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
|
+
}
|
|
@@ -5,17 +5,30 @@
|
|
|
5
5
|
//! onto its own error surface, and hosts the epoch-deadline callback
|
|
6
6
|
//! that raises the wall-clock `TimeoutTrap`. The classification is a
|
|
7
7
|
//! pure function over the error's downcast chain so it can be exercised
|
|
8
|
-
//! from `cargo test` without any frontend
|
|
9
|
-
//! themselves live in `crate::invocation` (where the limiter / callback
|
|
10
|
-
//! construct them).
|
|
8
|
+
//! from `cargo test` without any frontend.
|
|
11
9
|
|
|
12
|
-
use std::time::Instant;
|
|
10
|
+
use std::time::{Duration, Instant};
|
|
13
11
|
|
|
14
|
-
use wasmtime::{StoreContextMut, UpdateDeadline};
|
|
12
|
+
use wasmtime::{Store, StoreContextMut, UpdateDeadline};
|
|
15
13
|
|
|
16
|
-
use crate::invocation::
|
|
14
|
+
use crate::invocation::Invocation;
|
|
15
|
+
use crate::limiter::MemoryLimitTrap;
|
|
17
16
|
use kobako_runtime::error::{SetupError, Trap};
|
|
18
17
|
|
|
18
|
+
/// Marker error returned from the epoch-deadline callback when the
|
|
19
|
+
/// wall-clock deadline is exceeded. Downcast from the wasmtime trap
|
|
20
|
+
/// error to classify the failure as a timeout `Trap`.
|
|
21
|
+
#[derive(Debug)]
|
|
22
|
+
pub(crate) struct TimeoutTrap;
|
|
23
|
+
|
|
24
|
+
impl std::fmt::Display for TimeoutTrap {
|
|
25
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
26
|
+
write!(f, "wall-clock deadline exceeded")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl std::error::Error for TimeoutTrap {}
|
|
31
|
+
|
|
19
32
|
/// Epoch delta that keeps the deadline effectively unreachable when no
|
|
20
33
|
/// wall-clock cap is configured. Half the epoch range rather than
|
|
21
34
|
/// `u64::MAX`: wasmtime adds the delta to the engine's current epoch,
|
|
@@ -24,14 +37,9 @@ use kobako_runtime::error::{SetupError, Trap};
|
|
|
24
37
|
/// overflow checks).
|
|
25
38
|
pub(crate) const NO_TIMEOUT_EPOCH_DELTA: u64 = u64::MAX / 2;
|
|
26
39
|
|
|
27
|
-
///
|
|
28
|
-
///
|
|
29
|
-
///
|
|
30
|
-
/// next check by one tick of the process-wide epoch ticker. When the
|
|
31
|
-
/// deadline is `None` the callback should not fire under the normal
|
|
32
|
-
/// `Driver` invoke flow because
|
|
33
|
-
/// `NO_TIMEOUT_EPOCH_DELTA` is primed; returning the same long
|
|
34
|
-
/// extension keeps the callback inert as a defence in depth.
|
|
40
|
+
/// With no deadline the primed `NO_TIMEOUT_EPOCH_DELTA` keeps this from
|
|
41
|
+
/// firing; returning the same long extension keeps it inert as a defence
|
|
42
|
+
/// in depth.
|
|
35
43
|
pub(crate) fn epoch_deadline_callback(
|
|
36
44
|
ctx: StoreContextMut<'_, Invocation>,
|
|
37
45
|
) -> wasmtime::Result<UpdateDeadline> {
|
|
@@ -42,39 +50,42 @@ pub(crate) fn epoch_deadline_callback(
|
|
|
42
50
|
}
|
|
43
51
|
}
|
|
44
52
|
|
|
45
|
-
///
|
|
46
|
-
///
|
|
47
|
-
///
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
///
|
|
53
|
-
///
|
|
54
|
-
///
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
/// The message leaves out the ABI export symbol, so the Sandbox layer can
|
|
54
|
+
/// attach the caller's verb instead. A cap trap carries its own message,
|
|
55
|
+
/// since wasmtime's backtrace framing is noise there.
|
|
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> {
|
|
62
78
|
if let Some(t) = err.downcast_ref::<TimeoutTrap>() {
|
|
63
|
-
Trap::Timeout(t.to_string())
|
|
64
|
-
} else if let Some(t) = err.downcast_ref::<MemoryLimitTrap>() {
|
|
65
|
-
Trap::MemoryLimit(t.to_string())
|
|
79
|
+
Some(Trap::Timeout(t.to_string()))
|
|
66
80
|
} else {
|
|
67
|
-
|
|
81
|
+
err.downcast_ref::<MemoryLimitTrap>()
|
|
82
|
+
.map(|t| Trap::MemoryLimit(t.to_string()))
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
///
|
|
72
|
-
/// the
|
|
73
|
-
///
|
|
74
|
-
/// chain's root cause and would otherwise be dropped, making real guest
|
|
75
|
-
/// faults undiagnosable. Append the root cause unless the framing already
|
|
76
|
-
/// carries it. Pure so it can be exercised from `cargo test` without the
|
|
77
|
-
/// magnus surface.
|
|
86
|
+
/// wasmtime's `Display` shows only the backtrace framing; the real trap
|
|
87
|
+
/// reason is the chain's root cause and would otherwise be dropped, leaving
|
|
88
|
+
/// a guest fault undiagnosable.
|
|
78
89
|
fn other_trap_message(err: &wasmtime::Error) -> String {
|
|
79
90
|
let display = format!("{}", err);
|
|
80
91
|
let root = err.root_cause().to_string();
|
|
@@ -85,22 +96,18 @@ fn other_trap_message(err: &wasmtime::Error) -> String {
|
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
///
|
|
89
|
-
///
|
|
90
|
-
///
|
|
91
|
-
///
|
|
92
|
-
/// cap is dormant until an invocation arms it
|
|
93
|
-
/// (`Invocation::arm_memory_cap`) and the probe Store names an epoch
|
|
94
|
-
/// deadline it cannot reach, so the `trap_from` trap-class split does
|
|
95
|
-
/// not apply here.
|
|
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.
|
|
96
103
|
pub(crate) fn instantiate_err(err: wasmtime::Error) -> SetupError {
|
|
97
104
|
SetupError::Dead(format!("instantiate: {err}"))
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
#[cfg(test)]
|
|
101
108
|
mod tests {
|
|
102
|
-
use super::{other_trap_message,
|
|
103
|
-
use crate::
|
|
109
|
+
use super::{other_trap_message, to_trap, TimeoutTrap, NO_TIMEOUT_EPOCH_DELTA};
|
|
110
|
+
use crate::limiter::MemoryLimitTrap;
|
|
104
111
|
use kobako_runtime::error::Trap;
|
|
105
112
|
|
|
106
113
|
// The no-timeout priming delta is added to the engine's current
|
|
@@ -115,32 +122,32 @@ mod tests {
|
|
|
115
122
|
fn no_timeout_delta_survives_a_ticked_engine_epoch() {
|
|
116
123
|
let engine = crate::cache::shared_engine().expect("shared engine must be constructible");
|
|
117
124
|
engine.increment_epoch();
|
|
118
|
-
let mut store = wasmtime::Store::new(engine,
|
|
125
|
+
let mut store = wasmtime::Store::new(engine, ());
|
|
119
126
|
store.set_epoch_deadline(NO_TIMEOUT_EPOCH_DELTA);
|
|
120
127
|
}
|
|
121
128
|
|
|
122
129
|
// @behavior OC-032
|
|
123
130
|
#[test]
|
|
124
|
-
fn
|
|
131
|
+
fn to_trap_routes_timeout_trap_to_timeout() {
|
|
125
132
|
let err = wasmtime::Error::new(TimeoutTrap);
|
|
126
133
|
let expected = TimeoutTrap.to_string();
|
|
127
|
-
assert!(matches!(
|
|
134
|
+
assert!(matches!(to_trap(err), Trap::Timeout(msg) if msg == expected));
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
// @behavior OC-033
|
|
131
138
|
#[test]
|
|
132
|
-
fn
|
|
139
|
+
fn to_trap_routes_memory_limit_trap_to_memory_limit() {
|
|
133
140
|
let trap = MemoryLimitTrap::new(1 << 20, 1 << 19);
|
|
134
141
|
let expected = trap.to_string();
|
|
135
142
|
let err = wasmtime::Error::new(trap);
|
|
136
|
-
assert!(matches!(
|
|
143
|
+
assert!(matches!(to_trap(err), Trap::MemoryLimit(msg) if msg == expected));
|
|
137
144
|
}
|
|
138
145
|
|
|
139
146
|
// @behavior OC-028
|
|
140
147
|
#[test]
|
|
141
|
-
fn
|
|
148
|
+
fn to_trap_falls_back_to_other_for_unknown_errors() {
|
|
142
149
|
let err = wasmtime::Error::msg("some other wasmtime fault");
|
|
143
|
-
assert!(matches!(
|
|
150
|
+
assert!(matches!(to_trap(err), Trap::Other(_)));
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
// A guest hard trap reaches the host as a wasmtime error whose Display is
|