kobako 0.29.0 → 0.30.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 +217 -10
- data/Cargo.lock +139 -141
- data/README.md +96 -574
- data/ROADMAP.md +2 -1
- data/crates/kobako-runtime/CHANGELOG.md +269 -1
- data/crates/kobako-runtime/Cargo.toml +25 -15
- data/crates/kobako-runtime/README.md +7 -3
- data/crates/kobako-runtime/src/dispatch.rs +1 -1
- data/crates/kobako-runtime/src/endpoint/buffer.rs +192 -0
- data/crates/kobako-runtime/src/endpoint/capture.rs +115 -0
- data/crates/kobako-runtime/src/endpoint/dispatch.rs +253 -0
- data/crates/kobako-runtime/src/endpoint/invoke.rs +194 -0
- data/crates/kobako-runtime/src/endpoint/memory_cap.rs +238 -0
- data/crates/kobako-runtime/src/endpoint.rs +166 -0
- data/crates/kobako-runtime/src/filesystem/bind/fid.rs +82 -0
- data/crates/kobako-runtime/src/filesystem/{directory/spelling.rs → bind/lookup.rs} +91 -49
- data/crates/kobako-runtime/src/filesystem/bind/walk.rs +249 -0
- data/crates/kobako-runtime/src/filesystem/bind.rs +2665 -0
- data/crates/kobako-runtime/src/filesystem/overlay/file.rs +89 -0
- data/crates/kobako-runtime/src/filesystem/overlay/inode.rs +106 -0
- data/crates/kobako-runtime/src/filesystem/overlay/path.rs +285 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/copy_up.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/dir.rs +263 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/namei.rs +135 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack/readdir.rs +57 -0
- data/crates/kobako-runtime/src/filesystem/overlay/stack.rs +227 -734
- data/crates/kobako-runtime/src/filesystem/overlay.rs +2649 -1135
- data/crates/kobako-runtime/src/filesystem/path.rs +2 -41
- data/crates/kobako-runtime/src/filesystem/pipe.rs +221 -0
- data/crates/kobako-runtime/src/filesystem/rdir.rs +101 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/tmpfile.rs +32 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/whiteout.rs +85 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs/xattr.rs +22 -0
- data/crates/kobako-runtime/src/filesystem/tmpfs.rs +1985 -0
- data/crates/kobako-runtime/src/filesystem/wrap.rs +224 -0
- data/crates/kobako-runtime/src/filesystem.rs +21 -387
- data/crates/kobako-runtime/src/hashtable.rs +44 -0
- data/crates/kobako-runtime/src/lib.rs +11 -6
- data/crates/kobako-runtime/src/locking/seqlock.rs +66 -0
- data/crates/kobako-runtime/src/locking.rs +218 -0
- data/crates/kobako-runtime/src/mount.rs +31 -104
- data/crates/kobako-runtime/src/profile.rs +137 -1
- data/crates/kobako-runtime/src/sched.rs +210 -0
- data/crates/kobako-runtime/src/security.rs +696 -0
- data/crates/kobako-runtime/src/vfs/attr.rs +162 -0
- data/crates/kobako-runtime/src/vfs/cred.rs +334 -0
- data/crates/kobako-runtime/src/vfs/dentry.rs +1412 -0
- data/crates/kobako-runtime/src/vfs/errno.rs +153 -0
- data/crates/kobako-runtime/src/vfs/file.rs +1915 -0
- data/crates/kobako-runtime/src/vfs/inode.rs +429 -0
- data/crates/kobako-runtime/src/vfs/kern_path.rs +217 -0
- data/crates/kobako-runtime/src/vfs/mount.rs +59 -0
- data/crates/kobako-runtime/src/vfs/namei.rs +5708 -0
- data/crates/kobako-runtime/src/vfs/openat2.rs +116 -0
- data/crates/kobako-runtime/src/vfs/path.rs +64 -0
- data/crates/kobako-runtime/src/vfs/readdir.rs +163 -0
- data/crates/kobako-runtime/src/vfs/stat.rs +157 -0
- data/crates/kobako-runtime/src/vfs/super_block.rs +122 -0
- data/crates/kobako-runtime/src/vfs/utimes.rs +289 -0
- data/crates/kobako-runtime/src/vfs.rs +60 -0
- data/crates/kobako-runtime/tests/linux_baseline/calls.rs +171 -0
- data/crates/kobako-runtime/tests/linux_baseline/concurrent.rs +276 -0
- data/crates/kobako-runtime/tests/linux_baseline/kobako.rs +323 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux-baseline-expected.txt +16 -0
- data/crates/kobako-runtime/tests/linux_baseline/linux.rs +449 -0
- data/crates/kobako-runtime/tests/linux_baseline/main.rs +611 -0
- data/crates/kobako-runtime/tests/linux_baseline/permissions.rs +236 -0
- data/crates/kobako-transport/CHANGELOG.md +8 -2
- data/crates/kobako-transport/Cargo.toml +3 -3
- data/crates/kobako-transport/README.md +6 -5
- data/crates/kobako-transport/src/envelope/bytes.rs +33 -25
- data/crates/kobako-transport/src/envelope/call.rs +5 -5
- data/crates/kobako-transport/src/envelope/compile_reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/error_record.rs +3 -3
- data/crates/kobako-transport/src/envelope/fault.rs +2 -2
- data/crates/kobako-transport/src/envelope/outcome.rs +3 -3
- data/crates/kobako-transport/src/envelope/preamble.rs +12 -12
- data/crates/kobako-transport/src/envelope/reply.rs +2 -2
- data/crates/kobako-transport/src/envelope/request.rs +2 -2
- data/crates/kobako-transport/src/envelope/run.rs +1 -1
- data/crates/kobako-transport/src/envelope/unsupported.rs +2 -2
- data/crates/kobako-transport/src/envelope.rs +2 -2
- data/crates/kobako-wasmtime/CHANGELOG.md +154 -8
- data/crates/kobako-wasmtime/Cargo.toml +15 -14
- data/crates/kobako-wasmtime/README.md +9 -5
- data/crates/kobako-wasmtime/src/abi.rs +12 -20
- data/crates/kobako-wasmtime/src/ambient.rs +1 -2
- data/crates/kobako-wasmtime/src/cache.rs +5 -5
- data/crates/kobako-wasmtime/src/config.rs +22 -7
- data/crates/kobako-wasmtime/src/descriptors.rs +133 -100
- data/crates/kobako-wasmtime/src/driver.rs +29 -96
- data/crates/kobako-wasmtime/src/exports.rs +5 -16
- data/crates/kobako-wasmtime/src/guest.rs +38 -0
- data/crates/kobako-wasmtime/src/imports.rs +14 -8
- data/crates/kobako-wasmtime/src/instance_pre.rs +3 -3
- data/crates/kobako-wasmtime/src/invocation.rs +26 -46
- data/crates/kobako-wasmtime/src/lib.rs +1 -4
- data/crates/kobako-wasmtime/src/limiter.rs +6 -230
- data/crates/kobako-wasmtime/src/preview1/differential/mount.rs +69 -46
- data/crates/kobako-wasmtime/src/preview1/differential.rs +121 -7
- data/crates/kobako-wasmtime/src/preview1/errno.rs +74 -206
- data/crates/kobako-wasmtime/src/preview1/fd.rs +1248 -500
- data/crates/kobako-wasmtime/src/preview1/memory.rs +117 -150
- data/crates/kobako-wasmtime/src/preview1/mount_view/wrap.rs +211 -0
- data/crates/kobako-wasmtime/src/preview1/mount_view.rs +1939 -672
- data/crates/kobako-wasmtime/src/preview1/path.rs +230 -349
- data/crates/kobako-wasmtime/src/preview1/poll.rs +148 -100
- data/crates/kobako-wasmtime/src/preview1/sock.rs +6 -23
- data/crates/kobako-wasmtime/src/preview1/testsuite-expected.txt +6 -0
- data/crates/kobako-wasmtime/src/preview1/testsuite.rs +157 -0
- data/crates/kobako-wasmtime/src/preview1.rs +464 -413
- data/crates/kobako-wasmtime/src/trap.rs +9 -9
- data/crates/kobako-wasmtime/src/wasi.rs +53 -37
- data/crates/kobako-wasmtime/tests/{guest_file.rs → integration/guest_file.rs} +232 -201
- data/crates/kobako-wasmtime/tests/integration/main.rs +29 -0
- data/crates/kobako-wasmtime/tests/{preamble_flags.rs → integration/preamble_flags.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{snippet_compile.rs → integration/snippet_compile.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unanswered_call.rs → integration/unanswered_call.rs} +1 -1
- data/crates/kobako-wasmtime/tests/{unsupported_call.rs → integration/unsupported_call.rs} +1 -1
- data/crates/kobako-wasmtime/witx/typenames.witx +750 -0
- data/crates/kobako-wasmtime/witx/wasi_snapshot_preview1.witx +441 -0
- data/data/kobako.wasm +0 -0
- data/ext/kobako/Cargo.toml +1 -1
- data/ext/kobako/src/runtime/bridge.rs +1 -1
- data/ext/kobako/src/runtime/errors.rs +1 -1
- data/ext/kobako/src/runtime/escape.rs +28 -14
- data/ext/kobako/src/runtime/filesystem/grants.rs +21 -5
- data/ext/kobako/src/runtime/filesystem/ruby.rs +464 -156
- data/ext/kobako/src/runtime/filesystem.rs +109 -137
- data/ext/kobako/src/runtime/gvl.rs +43 -8
- data/ext/kobako/src/runtime/snapshot.rs +1 -1
- data/ext/kobako/src/runtime.rs +6 -1
- data/lib/kobako/catalog/extensions.rb +5 -5
- data/lib/kobako/catalog/flags.rb +2 -2
- data/lib/kobako/catalog/handles.rb +6 -6
- data/lib/kobako/catalog/mount.rb +2 -10
- data/lib/kobako/catalog/services.rb +2 -2
- data/lib/kobako/catalog/snippets.rb +7 -7
- data/lib/kobako/catalog.rb +1 -1
- data/lib/kobako/codec/decoder.rb +10 -10
- data/lib/kobako/codec/ext_types.rb +3 -5
- data/lib/kobako/codec/handle_walk.rb +4 -6
- data/lib/kobako/codec/nesting.rb +7 -7
- data/lib/kobako/codec/utils.rb +1 -1
- data/lib/kobako/codec/value_budget.rb +3 -3
- data/lib/kobako/context.rb +5 -5
- data/lib/kobako/filesystem.rb +44 -13
- data/lib/kobako/payload/arguments.rb +2 -2
- data/lib/kobako/pool.rb +4 -4
- data/lib/kobako/sandbox.rb +28 -28
- data/lib/kobako/sandbox_options.rb +2 -3
- data/lib/kobako/snippet/bytecode.rb +1 -1
- data/lib/kobako/snippet/source.rb +1 -1
- data/lib/kobako/transport/dispatcher.rb +5 -5
- data/lib/kobako/transport/exposure.rb +2 -2
- data/lib/kobako/transport/reflection.rb +7 -7
- data/lib/kobako/transport/run.rb +5 -5
- data/lib/kobako/transport/yielder.rb +1 -1
- data/lib/kobako/version.rb +1 -1
- data/lib/kobako.rb +34 -0
- data/release-please-config.json +1 -0
- data/sig/kobako/catalog/extensions.rbs +3 -3
- data/sig/kobako/catalog/handles.rbs +3 -3
- data/sig/kobako/catalog/mount.rbs +0 -4
- data/sig/kobako/catalog/services.rbs +1 -1
- data/sig/kobako/catalog/snippets.rbs +3 -3
- data/sig/kobako/catalog.rbs +1 -1
- data/sig/kobako/codec/decoder.rbs +1 -1
- data/sig/kobako/codec/handle_walk.rbs +1 -1
- data/sig/kobako/codec/nesting.rbs +2 -2
- data/sig/kobako/codec/utils.rbs +1 -1
- data/sig/kobako/codec/value_budget.rbs +1 -1
- data/sig/kobako/codec.rbs +1 -2
- data/sig/kobako/context.rbs +1 -1
- data/sig/kobako/filesystem.rbs +19 -14
- data/sig/kobako/guest_yielder.rbs +1 -1
- data/sig/kobako/payload/arguments.rbs +1 -1
- data/sig/kobako/pool.rbs +2 -2
- data/sig/kobako/runtime.rbs +1 -1
- data/sig/kobako/sandbox.rbs +3 -5
- data/sig/kobako/sandbox_options.rbs +1 -1
- data/sig/kobako/transport/dispatcher.rbs +2 -2
- data/sig/kobako/transport/exposure.rbs +1 -1
- data/sig/kobako/transport/reflection.rbs +2 -2
- data/sig/kobako/transport/run.rbs +3 -3
- data/sig/kobako/transport.rbs +1 -1
- metadata +67 -19
- data/crates/kobako-runtime/src/filesystem/directory.rs +0 -1082
- data/crates/kobako-runtime/src/filesystem/memory.rs +0 -1100
- data/crates/kobako-runtime/src/filesystem/overlay/exclusive.rs +0 -88
- data/crates/kobako-runtime/src/filesystem/temporary.rs +0 -167
- data/crates/kobako-wasmtime/src/capture.rs +0 -96
- data/crates/kobako-wasmtime/src/dispatch.rs +0 -125
- data/crates/kobako-wasmtime/src/guest_mem.rs +0 -271
- data/crates/kobako-wasmtime/src/io.rs +0 -89
- data/crates/kobako-wasmtime/src/preview1/args.rs +0 -60
- data/crates/kobako-wasmtime/src/preview1/beneath.rs +0 -188
- /data/crates/kobako-wasmtime/tests/{compile_posture.rs → integration/compile_posture.rs} +0 -0
- /data/crates/kobako-wasmtime/tests/{support → integration/support}/mod.rs +0 -0
|
@@ -3,123 +3,106 @@
|
|
|
3
3
|
//!
|
|
4
4
|
//! kobako answers every preview1 call that reads this table, so the guest
|
|
5
5
|
//! sees one table and no call falls through to a second one that
|
|
6
|
-
//! disagrees. Numbering follows
|
|
7
|
-
//! before a fresh one is taken.
|
|
6
|
+
//! disagrees. Numbering follows Linux's `alloc_fd`: the lowest closed number
|
|
7
|
+
//! is reused before a fresh one is taken.
|
|
8
8
|
|
|
9
9
|
use std::collections::{BTreeMap, BTreeSet};
|
|
10
10
|
use std::sync::Arc;
|
|
11
11
|
|
|
12
|
-
use kobako_runtime::filesystem::
|
|
12
|
+
use kobako_runtime::filesystem::FilesystemError;
|
|
13
13
|
use kobako_runtime::mount::Mount;
|
|
14
|
-
use
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
pub(crate) enum Descriptor {
|
|
18
|
-
/// Descriptor 0, carrying the Preamble.
|
|
19
|
-
Stdin(MemoryInputPipe),
|
|
20
|
-
/// Descriptor 1 or 2, a capture pipe the guest writes a Channel into.
|
|
21
|
-
Channel(MemoryOutputPipe),
|
|
22
|
-
/// A directory beneath a Mount; a preopen is the Mount's own root.
|
|
23
|
-
Directory(OpenDirectory),
|
|
24
|
-
File(OpenFile),
|
|
25
|
-
}
|
|
14
|
+
use kobako_runtime::profile::Profile;
|
|
15
|
+
use kobako_runtime::security::{restrict_self, WalkGuard};
|
|
16
|
+
use kobako_runtime::vfs::{Cred, File};
|
|
26
17
|
|
|
27
|
-
/// What
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
ino: u64,
|
|
18
|
+
/// What one descriptor stands for, as Linux's fd table holds a `struct file`.
|
|
19
|
+
pub(crate) struct Descriptor {
|
|
20
|
+
pub(crate) file: File,
|
|
21
|
+
/// The guest path a preopen names; a Mount's own root has one.
|
|
22
|
+
pub(crate) preopen_path: Option<String>,
|
|
33
23
|
}
|
|
34
24
|
|
|
35
|
-
impl
|
|
36
|
-
/// The
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
25
|
+
impl Descriptor {
|
|
26
|
+
/// The descriptor of `file`, naming no preopen.
|
|
27
|
+
pub(crate) fn for_file(file: File) -> Self {
|
|
28
|
+
Self {
|
|
29
|
+
file,
|
|
30
|
+
preopen_path: None,
|
|
31
|
+
}
|
|
43
32
|
}
|
|
44
33
|
}
|
|
45
34
|
|
|
46
|
-
pub(crate) struct OpenDirectory {
|
|
47
|
-
pub(crate) mount: Arc<Mount>,
|
|
48
|
-
/// The directory's path relative to the mount's root.
|
|
49
|
-
pub(crate) path: String,
|
|
50
|
-
/// The identity the directory had when it was opened, so one made in
|
|
51
|
-
/// its place is not taken for it; without one it follows its path.
|
|
52
|
-
pub(crate) identity: Option<Identity>,
|
|
53
|
-
/// What keeps that identity from passing to a directory made in this
|
|
54
|
-
/// one's place while the descriptor is open.
|
|
55
|
-
pub(crate) _hold: Option<Hold>,
|
|
56
|
-
pub(crate) preopen: bool,
|
|
57
|
-
/// The entries a listing began with, held until it reaches its end, so
|
|
58
|
-
/// each later call reads on without listing the directory again.
|
|
59
|
-
pub(crate) listing: Option<Vec<DirEntry>>,
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
pub(crate) struct OpenFile {
|
|
63
|
-
pub(crate) file: Box<dyn File>,
|
|
64
|
-
pub(crate) position: u64,
|
|
65
|
-
pub(crate) append: bool,
|
|
66
|
-
pub(crate) readable: bool,
|
|
67
|
-
pub(crate) writable: bool,
|
|
68
|
-
/// The Mount it was opened beneath, whose permission to write answers a
|
|
69
|
-
/// wait to write and a resize, as wasmtime-wasi answers both by its
|
|
70
|
-
/// preopen's permissions.
|
|
71
|
-
pub(crate) mount: Arc<Mount>,
|
|
72
|
-
}
|
|
73
|
-
|
|
74
35
|
pub(crate) struct Descriptors {
|
|
75
36
|
used: BTreeMap<u32, Descriptor>,
|
|
76
37
|
free: BTreeSet<u32>,
|
|
77
38
|
limit: Option<usize>,
|
|
39
|
+
/// Who the guest's calls are made for, as a task holds its `cred`.
|
|
40
|
+
cred: Arc<Cred>,
|
|
41
|
+
/// What a descriptor's call asks before it starts.
|
|
42
|
+
walk_guard: WalkGuard,
|
|
43
|
+
/// The isolation the guest runs under, which decides what it sees of
|
|
44
|
+
/// the times a Filesystem reports.
|
|
45
|
+
profile: Profile,
|
|
78
46
|
}
|
|
79
47
|
|
|
80
48
|
impl Descriptors {
|
|
81
49
|
/// The table every invocation starts from: stdin, then the two
|
|
82
50
|
/// Channels, then one preopen per Mount from descriptor 3. It holds at
|
|
83
51
|
/// most `limit` descriptors, those included, as a process's
|
|
84
|
-
/// `RLIMIT_NOFILE` counts every one it holds.
|
|
52
|
+
/// `RLIMIT_NOFILE` counts every one it holds. Each Mount's root opens as
|
|
53
|
+
/// `opener`, the Cred the Driver prepared, before its Policy applies.
|
|
85
54
|
pub(crate) fn new(
|
|
86
|
-
stdin:
|
|
87
|
-
stdout:
|
|
88
|
-
stderr:
|
|
55
|
+
stdin: File,
|
|
56
|
+
stdout: File,
|
|
57
|
+
stderr: File,
|
|
89
58
|
mounts: Vec<Mount>,
|
|
90
59
|
limit: Option<usize>,
|
|
91
|
-
|
|
60
|
+
opener: Arc<Cred>,
|
|
61
|
+
profile: Profile,
|
|
62
|
+
) -> Result<Self, FilesystemError> {
|
|
92
63
|
let mut used = BTreeMap::from([
|
|
93
|
-
(0, Descriptor::
|
|
94
|
-
(1, Descriptor::
|
|
95
|
-
(2, Descriptor::
|
|
64
|
+
(0, Descriptor::for_file(stdin)),
|
|
65
|
+
(1, Descriptor::for_file(stdout)),
|
|
66
|
+
(2, Descriptor::for_file(stderr)),
|
|
96
67
|
]);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let identity = filesystem
|
|
102
|
-
.metadata("")
|
|
103
|
-
.ok()
|
|
104
|
-
.and_then(|root| Identity::of(&root));
|
|
105
|
-
let hold = filesystem.hold_directory("").ok().flatten();
|
|
68
|
+
// Each Mount's root opens before its Policy applies, as a Linux task
|
|
69
|
+
// opens what it keeps and then calls `landlock_restrict_self`, so a
|
|
70
|
+
// Policy judges only what the guest asks for.
|
|
71
|
+
for (fd, mount) in (3..).zip(&mounts) {
|
|
106
72
|
used.insert(
|
|
107
73
|
fd,
|
|
108
|
-
Descriptor
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
_hold: hold,
|
|
113
|
-
preopen: true,
|
|
114
|
-
listing: None,
|
|
115
|
-
}),
|
|
74
|
+
Descriptor {
|
|
75
|
+
file: File::root(mount.mnt(), &opener)?,
|
|
76
|
+
preopen_path: Some(mount.at().to_string()),
|
|
77
|
+
},
|
|
116
78
|
);
|
|
117
79
|
}
|
|
118
|
-
|
|
80
|
+
let cred = restrict_self(&opener, &mounts);
|
|
81
|
+
let walk_guard = WalkGuard::new(&mounts);
|
|
82
|
+
Ok(Self {
|
|
119
83
|
used,
|
|
120
84
|
free: BTreeSet::new(),
|
|
121
85
|
limit,
|
|
122
|
-
|
|
86
|
+
cred,
|
|
87
|
+
walk_guard,
|
|
88
|
+
profile,
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Who the guest's calls are made for.
|
|
93
|
+
pub(crate) fn cred(&self) -> &Arc<Cred> {
|
|
94
|
+
&self.cred
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// What every call through a descriptor asks first, so a Policy judges the
|
|
98
|
+
/// path it stands at.
|
|
99
|
+
pub(crate) fn walk_guard(&self) -> &WalkGuard {
|
|
100
|
+
&self.walk_guard
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// The isolation the guest runs under.
|
|
104
|
+
pub(crate) fn profile(&self) -> Profile {
|
|
105
|
+
self.profile
|
|
123
106
|
}
|
|
124
107
|
|
|
125
108
|
pub(crate) fn get(&self, fd: u32) -> Option<&Descriptor> {
|
|
@@ -137,31 +120,81 @@ impl Descriptors {
|
|
|
137
120
|
Some(descriptor)
|
|
138
121
|
}
|
|
139
122
|
|
|
140
|
-
/// Place `descriptor` at `fd`, a number the caller has just freed
|
|
123
|
+
/// Place `descriptor` at `fd`, a number the caller has just freed or
|
|
124
|
+
/// `lowest_unused` found, as Linux's `fd_install` does.
|
|
141
125
|
pub(crate) fn place(&mut self, fd: u32, descriptor: Descriptor) {
|
|
142
126
|
self.free.remove(&fd);
|
|
143
127
|
self.used.insert(fd, descriptor);
|
|
144
128
|
}
|
|
145
129
|
|
|
146
|
-
///
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
/// the highest in use; `None` when the table is full.
|
|
153
|
-
pub(crate) fn push(&mut self, descriptor: Descriptor) -> Option<u32> {
|
|
154
|
-
if self.is_full() {
|
|
130
|
+
/// The number the next descriptor takes, the lowest freed one or the one
|
|
131
|
+
/// past the highest in use, as Linux's `get_unused_fd_flags` finds it;
|
|
132
|
+
/// `None` when the table holds as many as it may. Nothing is placed
|
|
133
|
+
/// until `place`, as `fd_install` fills what was found.
|
|
134
|
+
pub(crate) fn lowest_unused(&self) -> Option<u32> {
|
|
135
|
+
if self.limit.is_some_and(|limit| self.used.len() >= limit) {
|
|
155
136
|
return None;
|
|
156
137
|
}
|
|
157
|
-
|
|
158
|
-
Some(fd) => fd,
|
|
138
|
+
match self.free.first() {
|
|
139
|
+
Some(fd) => Some(*fd),
|
|
159
140
|
None => match self.used.last_key_value() {
|
|
160
|
-
Some((last, _)) => last.checked_add(1)
|
|
161
|
-
None => 0,
|
|
141
|
+
Some((last, _)) => last.checked_add(1),
|
|
142
|
+
None => Some(0),
|
|
162
143
|
},
|
|
163
|
-
}
|
|
164
|
-
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/// Add `descriptor` under the number `lowest_unused` finds; `None` when
|
|
148
|
+
/// the table is full.
|
|
149
|
+
#[cfg(test)]
|
|
150
|
+
pub(crate) fn push(&mut self, descriptor: Descriptor) -> Option<u32> {
|
|
151
|
+
let fd = self.lowest_unused()?;
|
|
152
|
+
self.place(fd, descriptor);
|
|
165
153
|
Some(fd)
|
|
166
154
|
}
|
|
167
155
|
}
|
|
156
|
+
|
|
157
|
+
#[cfg(test)]
|
|
158
|
+
mod tests {
|
|
159
|
+
use super::*;
|
|
160
|
+
use kobako_runtime::filesystem::{create_pipe_files, PipeInodeInfo};
|
|
161
|
+
use kobako_runtime::security::prepare_creds_within;
|
|
162
|
+
|
|
163
|
+
fn pipe_end() -> File {
|
|
164
|
+
create_pipe_files(&PipeInodeInfo::new(1), &prepare_creds_within(&[], None)).1
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// @behavior RT-189
|
|
168
|
+
#[test]
|
|
169
|
+
fn a_new_descriptor_takes_the_lowest_closed_number() {
|
|
170
|
+
let mut table = Descriptors::new(
|
|
171
|
+
pipe_end(),
|
|
172
|
+
pipe_end(),
|
|
173
|
+
pipe_end(),
|
|
174
|
+
Vec::new(),
|
|
175
|
+
None,
|
|
176
|
+
prepare_creds_within(&[], None),
|
|
177
|
+
Profile::Hermetic,
|
|
178
|
+
)
|
|
179
|
+
.unwrap_or_else(|_| panic!("a table holding no Mount must make"));
|
|
180
|
+
let mut open = || {
|
|
181
|
+
table
|
|
182
|
+
.push(Descriptor::for_file(pipe_end()))
|
|
183
|
+
.expect("a table with no limit takes every descriptor")
|
|
184
|
+
};
|
|
185
|
+
let numbers = [open(), open(), open()];
|
|
186
|
+
assert_eq!(numbers, [3, 4, 5], "descriptors must number from 3");
|
|
187
|
+
table.remove(3);
|
|
188
|
+
table.remove(4);
|
|
189
|
+
assert_eq!(
|
|
190
|
+
(
|
|
191
|
+
table.push(Descriptor::for_file(pipe_end())),
|
|
192
|
+
table.push(Descriptor::for_file(pipe_end())),
|
|
193
|
+
table.push(Descriptor::for_file(pipe_end())),
|
|
194
|
+
),
|
|
195
|
+
(Some(3), Some(4), Some(6)),
|
|
196
|
+
"path_open's descriptors must take the lowest closed number first, as Linux's alloc_fd \
|
|
197
|
+
does, and then the one past the highest"
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -14,25 +14,27 @@ use wasmtime::{
|
|
|
14
14
|
AsContextMut, InstancePre as WtInstancePre, ResourceLimiter, Store as WtStore, TypedFunc,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
use crate::cache::
|
|
17
|
+
use crate::cache::load_engine;
|
|
18
18
|
use crate::config::Config;
|
|
19
19
|
use crate::exports::{self, Exports};
|
|
20
|
+
use crate::guest::Guest;
|
|
20
21
|
use crate::invocation::Invocation;
|
|
21
22
|
use crate::wasi::Wasi;
|
|
22
|
-
use crate::{
|
|
23
|
+
use crate::{instance_pre, trap};
|
|
23
24
|
use kobako_runtime::dispatch::DispatchHandler;
|
|
25
|
+
use kobako_runtime::endpoint;
|
|
24
26
|
use kobako_runtime::error::{InvokeError, SetupError, Trap};
|
|
25
27
|
use kobako_runtime::grant::{Grant, GrantKind};
|
|
26
28
|
use kobako_runtime::profile::Profile;
|
|
27
29
|
use kobako_runtime::runtime::{Entry, Runtime as ContractRuntime};
|
|
28
30
|
use kobako_runtime::snapshot::{Capture, Completion, Snapshot, Usage};
|
|
29
|
-
use kobako_transport::envelope::
|
|
31
|
+
use kobako_transport::envelope::Preamble;
|
|
30
32
|
|
|
31
33
|
/// The wasmtime execution unit behind one sandbox runtime.
|
|
32
34
|
pub struct Driver {
|
|
33
35
|
// Pre-linked instantiation template (import wiring, type checks,
|
|
34
36
|
// and the ABI version check done once in
|
|
35
|
-
// `instance_pre::
|
|
37
|
+
// `instance_pre::load_instance_pre`). Every invocation
|
|
36
38
|
// instantiates a fresh instance from it and discards the whole
|
|
37
39
|
// Store afterwards — the per-invocation instance discipline.
|
|
38
40
|
instance_pre: WtInstancePre<Invocation>,
|
|
@@ -46,7 +48,7 @@ impl Driver {
|
|
|
46
48
|
/// neither leaves the driver.
|
|
47
49
|
pub fn new(path: &Path, config: Config) -> Result<Self, SetupError> {
|
|
48
50
|
Ok(Self {
|
|
49
|
-
instance_pre: instance_pre::
|
|
51
|
+
instance_pre: instance_pre::load_instance_pre(path, config.setup_timeout)?,
|
|
50
52
|
config,
|
|
51
53
|
})
|
|
52
54
|
}
|
|
@@ -56,7 +58,7 @@ impl Driver {
|
|
|
56
58
|
/// `timeout` as the export call does.
|
|
57
59
|
fn new_store(&self, wasi: Wasi) -> Result<WtStore<Invocation>, SetupError> {
|
|
58
60
|
let mut store = WtStore::new(
|
|
59
|
-
|
|
61
|
+
load_engine()?,
|
|
60
62
|
Invocation::new(self.config.memory_limit, wasi),
|
|
61
63
|
);
|
|
62
64
|
store.limiter(|state: &mut Invocation| -> &mut dyn ResourceLimiter { state.limiter_mut() });
|
|
@@ -96,7 +98,12 @@ impl Driver {
|
|
|
96
98
|
Results: wasmtime::WasmResults,
|
|
97
99
|
{
|
|
98
100
|
self.arm_memory_cap(store, exports);
|
|
99
|
-
|
|
101
|
+
// A wait for a lock the call meets ends at the same deadline the guest's
|
|
102
|
+
// code does, so another Sandbox holding it cannot keep this one past it.
|
|
103
|
+
let deadline = store.data().deadline();
|
|
104
|
+
let result = kobako_runtime::sched::run_task(deadline, || {
|
|
105
|
+
export.call(store.as_context_mut(), params)
|
|
106
|
+
});
|
|
100
107
|
disarm_caps(store);
|
|
101
108
|
result
|
|
102
109
|
}
|
|
@@ -121,7 +128,7 @@ impl Driver {
|
|
|
121
128
|
usage.memory_peak = data.memory_peak();
|
|
122
129
|
let clip = |bytes, limit| {
|
|
123
130
|
let mut capture = Capture::default();
|
|
124
|
-
(capture.bytes, capture.truncated) =
|
|
131
|
+
(capture.bytes, capture.truncated) = endpoint::clip_capture(bytes, limit);
|
|
125
132
|
capture
|
|
126
133
|
};
|
|
127
134
|
Snapshot::new(
|
|
@@ -145,28 +152,20 @@ impl ContractRuntime for Driver {
|
|
|
145
152
|
handler: Option<Arc<dyn DispatchHandler>>,
|
|
146
153
|
grants: &[Grant],
|
|
147
154
|
) -> Result<Snapshot, InvokeError> {
|
|
148
|
-
let
|
|
155
|
+
let stdin =
|
|
156
|
+
endpoint::stdin_content(preamble).map_err(|msg| Trap::Other(msg.to_string()))?;
|
|
157
|
+
let wasi = Wasi::for_invocation(&self.config, stdin, grants)?;
|
|
149
158
|
let mut store = self.new_store(wasi)?;
|
|
150
159
|
if let Some(handler) = handler {
|
|
151
160
|
store.data_mut().bind_on_dispatch(handler);
|
|
152
161
|
}
|
|
153
162
|
let exports = self.instantiate(&mut store)?;
|
|
154
163
|
let downcall = exports::require(exports.downcall.as_ref())?;
|
|
155
|
-
let request =
|
|
156
|
-
|
|
157
|
-
Entry::Run {
|
|
158
|
-
entrypoint,
|
|
159
|
-
payload,
|
|
160
|
-
} => Downcall::Run(Run {
|
|
161
|
-
entrypoint: entrypoint.to_owned(),
|
|
162
|
-
payload,
|
|
163
|
-
}),
|
|
164
|
-
}
|
|
165
|
-
.encode();
|
|
166
|
-
let (ptr, len) = io::write_request(&mut store, &exports, &request)?;
|
|
164
|
+
let request = endpoint::request_of(entry);
|
|
165
|
+
let (ptr, len) = endpoint::write_request(&mut guest_of(&mut store), &request)?;
|
|
167
166
|
let called = self.call_with_caps(&mut store, &exports, downcall, (ptr, len));
|
|
168
167
|
let completion = match called {
|
|
169
|
-
Ok(packed) => match
|
|
168
|
+
Ok(packed) => match endpoint::read_outcome(&mut guest_of(&mut store), packed) {
|
|
170
169
|
Ok(outcome) => Completion::Outcome(outcome),
|
|
171
170
|
Err(t) => Completion::Trap(t),
|
|
172
171
|
},
|
|
@@ -192,93 +191,27 @@ impl ContractRuntime for Driver {
|
|
|
192
191
|
/// own that no script ever runs on, so what it answers carries
|
|
193
192
|
/// nothing a script could have written.
|
|
194
193
|
fn compile(&self, preamble: &Preamble) -> Option<Vec<Vec<u8>>> {
|
|
195
|
-
let wasi = Wasi::for_compile(&self.config,
|
|
194
|
+
let wasi = Wasi::for_compile(&self.config, endpoint::stdin_content(preamble).ok()?).ok()?;
|
|
196
195
|
let mut store = self.new_store(wasi).ok()?;
|
|
197
196
|
let exports = self.instantiate(&mut store).ok()?;
|
|
198
197
|
let downcall = exports::require(exports.downcall.as_ref()).ok()?;
|
|
199
198
|
let (ptr, len) =
|
|
200
|
-
|
|
199
|
+
endpoint::write_request(&mut guest_of(&mut store), &endpoint::compile_request())
|
|
200
|
+
.ok()?;
|
|
201
201
|
let packed = self
|
|
202
202
|
.call_with_caps(&mut store, &exports, downcall, (ptr, len))
|
|
203
203
|
.ok()?;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.filter(|bytecodes| replay_fits(preamble, bytecodes)),
|
|
207
|
-
CompileReply::Error(_) | CompileReply::Unsupported(_) => None,
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/// Each invocation sends the Preamble with every bytecode in place of its
|
|
213
|
-
/// source, so an answer that would carry it past the payload cap would trap
|
|
214
|
-
/// every invocation the source alone fits.
|
|
215
|
-
fn replay_fits(preamble: &Preamble, bytecodes: &[Vec<u8>]) -> bool {
|
|
216
|
-
let mut replay = preamble.clone();
|
|
217
|
-
let sources = replay
|
|
218
|
-
.snippets
|
|
219
|
-
.entries
|
|
220
|
-
.iter_mut()
|
|
221
|
-
.filter(|entry| matches!(entry, Snippet::Source { .. }));
|
|
222
|
-
for (entry, body) in sources.zip(bytecodes) {
|
|
223
|
-
*entry = Snippet::Bytecode { body: body.clone() };
|
|
204
|
+
let reply = endpoint::read_compile_reply(&mut guest_of(&mut store), packed).ok()?;
|
|
205
|
+
endpoint::compiled_bytecodes(preamble, reply)
|
|
224
206
|
}
|
|
225
|
-
guest_mem::checked_payload_len(replay.encode().len()).is_ok()
|
|
226
207
|
}
|
|
227
208
|
|
|
228
|
-
///
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
let sources = preamble
|
|
232
|
-
.snippets
|
|
233
|
-
.entries
|
|
234
|
-
.iter()
|
|
235
|
-
.filter(|entry| matches!(entry, Snippet::Source { .. }))
|
|
236
|
-
.count();
|
|
237
|
-
(bytecodes.len() == sources).then_some(bytecodes)
|
|
209
|
+
/// The instance the runtime's host ABI endpoint drives, through `store`.
|
|
210
|
+
fn guest_of(store: &mut WtStore<Invocation>) -> Guest<'_> {
|
|
211
|
+
Guest(store.as_context_mut())
|
|
238
212
|
}
|
|
239
213
|
|
|
240
214
|
fn disarm_caps(store: &mut WtStore<Invocation>) {
|
|
241
215
|
store.data_mut().stop_wall_clock();
|
|
242
216
|
store.data_mut().disarm_memory_cap();
|
|
243
217
|
}
|
|
244
|
-
|
|
245
|
-
#[cfg(test)]
|
|
246
|
-
mod tests {
|
|
247
|
-
use kobako_transport::envelope::Snippets;
|
|
248
|
-
|
|
249
|
-
use super::*;
|
|
250
|
-
|
|
251
|
-
/// One source between two bytecode snippets: a compile answers one
|
|
252
|
-
/// bytecode for it.
|
|
253
|
-
fn one_source() -> Preamble {
|
|
254
|
-
let mut preamble = Preamble::default();
|
|
255
|
-
preamble.snippets = Snippets {
|
|
256
|
-
entries: vec![
|
|
257
|
-
Snippet::Bytecode { body: vec![0xaa] },
|
|
258
|
-
Snippet::Source {
|
|
259
|
-
name: "Only".into(),
|
|
260
|
-
body: "1".into(),
|
|
261
|
-
},
|
|
262
|
-
Snippet::Bytecode { body: vec![0xbb] },
|
|
263
|
-
],
|
|
264
|
-
};
|
|
265
|
-
preamble
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// @behavior WE-089
|
|
269
|
-
#[test]
|
|
270
|
-
fn a_compiled_answer_of_another_length_leaves_nothing_compiled() {
|
|
271
|
-
let preamble = one_source();
|
|
272
|
-
|
|
273
|
-
assert_eq!(
|
|
274
|
-
[
|
|
275
|
-
one_per_source(&preamble, Vec::new()),
|
|
276
|
-
one_per_source(&preamble, vec![vec![0x01], vec![0x02]]),
|
|
277
|
-
one_per_source(&preamble, vec![vec![0x01]]),
|
|
278
|
-
],
|
|
279
|
-
[None, None, Some(vec![vec![0x01]])],
|
|
280
|
-
"a compiled answer through Driver#compile must be kept only when it carries one \
|
|
281
|
-
bytecode per source snippet"
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
@@ -10,19 +10,13 @@
|
|
|
10
10
|
|
|
11
11
|
use wasmtime::{AsContextMut, Instance as WtInstance, Memory, TypedFunc};
|
|
12
12
|
|
|
13
|
-
use
|
|
13
|
+
use kobako_runtime::endpoint::RUNTIME_INCOMPATIBLE;
|
|
14
14
|
use kobako_runtime::error::Trap;
|
|
15
15
|
|
|
16
|
-
/// For a required guest export that is absent or mistyped, wherever it is
|
|
17
|
-
/// looked up. Names neither the export nor a frontend's artifact path;
|
|
18
|
-
/// what a caller can do is rebuild the runtime.
|
|
19
|
-
pub(crate) const RUNTIME_INCOMPATIBLE: &str =
|
|
20
|
-
"the Sandbox runtime is incompatible; rebuild it against the kobako version in use";
|
|
21
|
-
|
|
22
16
|
/// The resolved host-driven export handles. Each is `Option` because test
|
|
23
17
|
/// fixtures (a minimal "ping" module) need not provide them; real
|
|
24
|
-
/// `kobako.wasm` always does, and the run path surfaces a `Trap`
|
|
25
|
-
///
|
|
18
|
+
/// `kobako.wasm` always does, and the run path surfaces a `Trap` when a
|
|
19
|
+
/// handle is `None`.
|
|
26
20
|
///
|
|
27
21
|
/// The handles are indices into the owning Store, not borrows of the
|
|
28
22
|
/// `Instance` — they stay valid for the Store's lifetime, which is why
|
|
@@ -49,11 +43,6 @@ impl Exports {
|
|
|
49
43
|
memory: instance.get_memory(&mut ctx, "memory"),
|
|
50
44
|
}
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
pub(crate) fn require_memory(&self) -> Result<Memory, Trap> {
|
|
54
|
-
self.memory
|
|
55
|
-
.ok_or_else(|| Trap::Other(SANDBOX_RUNTIME_NOT_KOBAKO.to_string()))
|
|
56
|
-
}
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
pub(crate) fn require<Params, Results>(
|
|
@@ -77,7 +66,7 @@ mod tests {
|
|
|
77
66
|
|
|
78
67
|
use wasmtime::ExternType;
|
|
79
68
|
|
|
80
|
-
use crate::cache::
|
|
69
|
+
use crate::cache::load_module;
|
|
81
70
|
|
|
82
71
|
const WASM: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../data/kobako.wasm");
|
|
83
72
|
|
|
@@ -98,7 +87,7 @@ mod tests {
|
|
|
98
87
|
);
|
|
99
88
|
return;
|
|
100
89
|
}
|
|
101
|
-
let module =
|
|
90
|
+
let module = load_module(path).expect("the Guest Binary must load");
|
|
102
91
|
|
|
103
92
|
let mut functions: Vec<&str> = module
|
|
104
93
|
.exports()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//! One wasmtime instance as the runtime's host ABI endpoint drives it, from the
|
|
2
|
+
//! invocation body's `Store` and from a host import's `Caller` alike.
|
|
3
|
+
|
|
4
|
+
use kobako_runtime::endpoint::{Calls, GuestInstance};
|
|
5
|
+
use kobako_runtime::error::Trap;
|
|
6
|
+
use wasmtime::StoreContextMut;
|
|
7
|
+
|
|
8
|
+
use crate::invocation::Invocation;
|
|
9
|
+
use crate::trap;
|
|
10
|
+
|
|
11
|
+
pub(crate) struct Guest<'a>(pub(crate) StoreContextMut<'a, Invocation>);
|
|
12
|
+
|
|
13
|
+
impl GuestInstance for Guest<'_> {
|
|
14
|
+
type Fault = wasmtime::Error;
|
|
15
|
+
|
|
16
|
+
fn calls(&mut self) -> &mut Calls<wasmtime::Error> {
|
|
17
|
+
self.0.data_mut().calls_mut()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fn memory(&mut self) -> Option<&mut [u8]> {
|
|
21
|
+
let memory = self.0.data().exports()?.memory?;
|
|
22
|
+
Some(memory.data_mut(&mut self.0))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn alloc(&mut self, len: u32) -> Option<wasmtime::Result<u32>> {
|
|
26
|
+
let alloc = self.0.data().exports()?.alloc?;
|
|
27
|
+
Some(alloc.call(&mut self.0, len))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
fn downcall(&mut self, ptr: i32, len: i32) -> Option<wasmtime::Result<u64>> {
|
|
31
|
+
let downcall = self.0.data().exports()?.downcall?;
|
|
32
|
+
Some(downcall.call(&mut self.0, (ptr, len)))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn cap_trap(fault: &wasmtime::Error) -> Option<Trap> {
|
|
36
|
+
trap::cap_trap(fault)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
//! calling Store, never from a Runtime, so the wiring is the same for
|
|
6
6
|
//! every Sandbox and `crate::instance_pre` can cache what it resolves.
|
|
7
7
|
|
|
8
|
-
use wasmtime::{Caller, Linker};
|
|
8
|
+
use wasmtime::{AsContextMut, Caller, Linker};
|
|
9
9
|
use wasmtime_wasi::p1;
|
|
10
10
|
|
|
11
|
-
use crate::cache::
|
|
11
|
+
use crate::cache::load_engine;
|
|
12
|
+
use crate::guest::Guest;
|
|
12
13
|
use crate::invocation::Invocation;
|
|
13
|
-
use crate::
|
|
14
|
+
use crate::preview1;
|
|
15
|
+
use kobako_runtime::endpoint::{self, UpcallEnd, ABANDONED};
|
|
14
16
|
use kobako_runtime::error::SetupError;
|
|
15
17
|
|
|
16
18
|
pub(crate) fn linker() -> Result<Linker<Invocation>, SetupError> {
|
|
17
|
-
let mut linker: Linker<Invocation> = Linker::new(
|
|
19
|
+
let mut linker: Linker<Invocation> = Linker::new(load_engine()?);
|
|
18
20
|
|
|
19
21
|
// Wire the wasmtime-wasi preview1 WASI imports against each
|
|
20
22
|
// invocation's own `Wasi` context, then answer every call that reads
|
|
@@ -26,9 +28,8 @@ pub(crate) fn linker() -> Result<Linker<Invocation>, SetupError> {
|
|
|
26
28
|
.and_then(|_| preview1::shadow(&mut linker))
|
|
27
29
|
.map_err(|e| SetupError::Dead(format!("failed to set up the WASI runtime: {e}")))?;
|
|
28
30
|
|
|
29
|
-
// `__kobako_upcall`, `(ptr: i32, len: i32) -> i64`
|
|
30
|
-
//
|
|
31
|
-
// answers.
|
|
31
|
+
// `__kobako_upcall`, `(ptr: i32, len: i32) -> i64` in the ABI;
|
|
32
|
+
// `endpoint::handle` owns what it answers.
|
|
32
33
|
linker
|
|
33
34
|
.func_wrap(
|
|
34
35
|
"env",
|
|
@@ -37,7 +38,12 @@ pub(crate) fn linker() -> Result<Linker<Invocation>, SetupError> {
|
|
|
37
38
|
req_ptr: i32,
|
|
38
39
|
req_len: i32|
|
|
39
40
|
-> wasmtime::Result<i64> {
|
|
40
|
-
|
|
41
|
+
let mut guest = Guest(caller.as_context_mut());
|
|
42
|
+
endpoint::handle(&mut guest, req_ptr, req_len).map_err(|end| match end {
|
|
43
|
+
UpcallEnd::Fault(trap) => trap,
|
|
44
|
+
UpcallEnd::Abandoned => wasmtime::Error::msg(ABANDONED),
|
|
45
|
+
UpcallEnd::Unwritable(reason) => wasmtime::Error::msg(reason),
|
|
46
|
+
})
|
|
41
47
|
},
|
|
42
48
|
)
|
|
43
49
|
.map_err(|e| SetupError::Dead(format!("failed to set up the host callback bridge: {e}")))?;
|
|
@@ -16,7 +16,7 @@ use std::time::Duration;
|
|
|
16
16
|
|
|
17
17
|
use wasmtime::InstancePre;
|
|
18
18
|
|
|
19
|
-
use crate::cache::
|
|
19
|
+
use crate::cache::load_module;
|
|
20
20
|
use crate::invocation::Invocation;
|
|
21
21
|
use crate::{abi, imports, trap};
|
|
22
22
|
use kobako_runtime::error::SetupError;
|
|
@@ -26,7 +26,7 @@ static INSTANCE_PRE_CACHE: OnceLock<Mutex<HashMap<PathBuf, InstancePre<Invocatio
|
|
|
26
26
|
|
|
27
27
|
/// The probe runs only on the first load of a path, so only that load's
|
|
28
28
|
/// `setup_timeout` bounds it; a path already verified is not set up again.
|
|
29
|
-
pub(crate) fn
|
|
29
|
+
pub(crate) fn load_instance_pre(
|
|
30
30
|
path: &Path,
|
|
31
31
|
setup_timeout: Option<Duration>,
|
|
32
32
|
) -> Result<InstancePre<Invocation>, SetupError> {
|
|
@@ -41,7 +41,7 @@ pub(crate) fn cached_instance_pre(
|
|
|
41
41
|
return Ok(pre);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
let module =
|
|
44
|
+
let module = load_module(path)?;
|
|
45
45
|
let linker = imports::linker()?;
|
|
46
46
|
let pre = linker
|
|
47
47
|
.instantiate_pre(&module)
|