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
data/lib/kobako/errors.rb
CHANGED
|
@@ -1,103 +1,49 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Top-level Kobako namespace.
|
|
4
3
|
module Kobako
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
#
|
|
10
|
-
# host: first the Wasm-trap layer, then the outcome-envelope tag.
|
|
11
|
-
# SetupError and PoolTimeoutError sit outside the invocation taxonomy and
|
|
12
|
-
# never pass through that attribution decision. Each class below
|
|
13
|
-
# documents its own layer.
|
|
14
|
-
|
|
15
|
-
# Base for all kobako-raised errors so callers that want to ignore the
|
|
16
|
-
# taxonomy can rescue a single class.
|
|
4
|
+
# Every Sandbox#eval or Sandbox#run returns, or raises exactly one of
|
|
5
|
+
# TrapError, SandboxError, or ServiceError. SetupError and PoolTimeoutError
|
|
6
|
+
# arise outside any invocation.
|
|
7
|
+
|
|
8
|
+
# The base of every error kobako raises.
|
|
17
9
|
class Error < StandardError; end
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
# rescue reads its captures and usage exactly as a successful caller reads
|
|
21
|
-
# them off the return value. Mixed only into the three invocation-outcome
|
|
22
|
-
# classes (+TrapError+ / +SandboxError+ / +ServiceError+ and their
|
|
23
|
-
# subclasses); +SetupError+ and +PoolTimeoutError+ arise outside any
|
|
24
|
-
# invocation and carry no Execution. Defaults to +nil+ — a pre-flight
|
|
25
|
-
# failure that ran no invocation leaves it unset.
|
|
26
|
-
module CarriesExecution
|
|
11
|
+
module Recorded # :nodoc:
|
|
27
12
|
attr_reader :execution
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
# raise site reads +raise error.with_execution(execution)+.
|
|
31
|
-
def with_execution(execution)
|
|
14
|
+
def with_execution(execution) # :nodoc:
|
|
32
15
|
@execution = execution
|
|
33
16
|
self
|
|
34
17
|
end
|
|
35
18
|
end
|
|
36
19
|
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
# Two named subclasses cover the configured per-invocation caps:
|
|
43
|
-
#
|
|
44
|
-
# * TimeoutError — wall-clock +timeout+ exceeded.
|
|
45
|
-
# * MemoryLimitError — guest +memory.grow+ would exceed
|
|
46
|
-
# +memory_limit+.
|
|
47
|
-
#
|
|
48
|
-
# Host Apps that only care about "guest is unrecoverable, discard the
|
|
49
|
-
# Sandbox" can rescue +TrapError+ and ignore the subclass; Host Apps that
|
|
50
|
-
# want to surface a specific reason to operators can rescue the subclass
|
|
51
|
-
# first.
|
|
20
|
+
# The engine stopped the guest: a trap, a cap reached, or an outcome that
|
|
21
|
+
# could not be read. The next invocation on the same Sandbox runs as usual.
|
|
22
|
+
# +execution+ is the failed run's Execution, so a rescue reads its captures
|
|
23
|
+
# and usage as a successful caller would; it is +nil+ when the failure came
|
|
24
|
+
# before the guest ran.
|
|
52
25
|
class TrapError < Error
|
|
53
|
-
include
|
|
26
|
+
include Recorded
|
|
54
27
|
end
|
|
55
28
|
|
|
56
|
-
#
|
|
57
|
-
# +entry_time + timeout+ passed and the next guest wasm safepoint
|
|
58
|
-
# trapped. The Sandbox is unrecoverable after this point; discard and
|
|
59
|
-
# recreate before another execution.
|
|
29
|
+
# The invocation ran past its +timeout+.
|
|
60
30
|
class TimeoutError < TrapError; end
|
|
61
31
|
|
|
62
|
-
#
|
|
63
|
-
# linear memory past the configured +memory_limit+. The Sandbox is
|
|
64
|
-
# unrecoverable after this point; discard and recreate before another
|
|
65
|
-
# execution.
|
|
32
|
+
# The invocation grew guest memory past its +memory_limit+.
|
|
66
33
|
class MemoryLimitError < TrapError; end
|
|
67
34
|
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
# an unreadable artifact, bytes that are not a valid Wasm module, or
|
|
72
|
-
# engine / linker / instantiation setup failure. Construction
|
|
73
|
-
# is not an invocation, so +SetupError+ sits beside the invocation
|
|
74
|
-
# taxonomy under +Kobako::Error+ rather than under +TrapError+: no
|
|
75
|
-
# Sandbox is produced, so the +TrapError+ "discard and recreate"
|
|
76
|
-
# recovery contract does not apply.
|
|
35
|
+
# A Sandbox could not be built: the Guest Binary is unreadable or not a
|
|
36
|
+
# valid module, the runtime failed to start, or it provides less isolation
|
|
37
|
+
# than +profile+ asks for.
|
|
77
38
|
class SetupError < Error; end
|
|
78
39
|
|
|
79
|
-
# The
|
|
80
|
-
# the Guest Binary artifact is absent at +wasm_path+ — the pre-build
|
|
81
|
-
# state on a fresh clone before +bundle exec rake compile+. Host Apps
|
|
82
|
-
# that only need "the Sandbox could not be set up" rescue +SetupError+;
|
|
83
|
-
# those wanting to special-case the unbuilt-artifact state rescue
|
|
84
|
-
# +ModuleNotBuiltError+ first.
|
|
40
|
+
# The Guest Binary is missing at +wasm_path+.
|
|
85
41
|
class ModuleNotBuiltError < SetupError; end
|
|
86
42
|
|
|
87
|
-
|
|
88
|
-
# from a decoded guest exception — its +origin+, original +klass+, and
|
|
89
|
-
# +backtrace_lines+ — so a Host App can inspect a failure beyond its
|
|
90
|
-
# message. Mixed into both rather than promoted to a shared superclass
|
|
91
|
-
# because +SandboxError+ and +ServiceError+ sit in distinct branches of
|
|
92
|
-
# the invocation-outcome taxonomy under +Kobako::Error+.
|
|
93
|
-
#
|
|
94
|
-
# Data specific to one kind of failure rides a named reader on the
|
|
95
|
-
# subclass that failure raises, the way Ruby pairs NameError#name with
|
|
96
|
-
# +NameError#local_variables+ — see UndefinedEntrypointError.
|
|
97
|
-
module Diagnosable
|
|
43
|
+
module Diagnosable # :nodoc:
|
|
98
44
|
attr_reader :origin, :klass, :backtrace_lines
|
|
99
45
|
|
|
100
|
-
def initialize(message, origin: nil, klass: nil, backtrace_lines: nil)
|
|
46
|
+
def initialize(message, origin: nil, klass: nil, backtrace_lines: nil) # :nodoc:
|
|
101
47
|
super(message)
|
|
102
48
|
@origin = origin
|
|
103
49
|
@klass = klass
|
|
@@ -105,93 +51,72 @@ module Kobako
|
|
|
105
51
|
end
|
|
106
52
|
end
|
|
107
53
|
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
#
|
|
54
|
+
# The guest failed: the script raised or did not compile, the Guest Binary
|
|
55
|
+
# could not start the run, or what it produced could not be read.
|
|
56
|
+
# +origin+ (<tt>"sandbox"</tt> or <tt>"service"</tt>), +klass+ and
|
|
57
|
+
# +backtrace_lines+ say where it came from, which class raised it, and its
|
|
58
|
+
# backtrace. +execution+ is the failed run's Execution, as on TrapError.
|
|
112
59
|
class SandboxError < Error
|
|
113
60
|
include Diagnosable
|
|
114
|
-
include
|
|
61
|
+
include Recorded
|
|
115
62
|
end
|
|
116
63
|
|
|
117
|
-
# Service
|
|
118
|
-
#
|
|
119
|
-
# rescue. The base class covers a Service that ran and raised; the two
|
|
120
|
-
# subclasses below cover the calls that never reached one, so a Host App
|
|
121
|
-
# routes them apart with +rescue+ instead of by reading the message.
|
|
64
|
+
# A Service call failed and the guest left it unrescued. It carries
|
|
65
|
+
# +origin+, +klass+, +backtrace_lines+ and +execution+ as SandboxError does.
|
|
122
66
|
class ServiceError < Error
|
|
123
67
|
include Diagnosable
|
|
124
|
-
include
|
|
68
|
+
include Recorded
|
|
125
69
|
end
|
|
126
70
|
|
|
127
|
-
# The
|
|
128
|
-
#
|
|
129
|
-
#
|
|
130
|
-
# surface. The causes stay indistinguishable — an opaque target must
|
|
131
|
-
# disclose nothing about which methods it defines — so what a Host App
|
|
132
|
-
# learns is that this call will not succeed by being retried.
|
|
71
|
+
# The call reached no Service method: nothing is bound there, the Handle
|
|
72
|
+
# is not live, or the guest may not call that method. The causes look the
|
|
73
|
+
# same on purpose, so a target discloses nothing about what it defines.
|
|
133
74
|
class NoServiceError < ServiceError; end
|
|
134
75
|
|
|
135
|
-
# The
|
|
136
|
-
# method
|
|
137
|
-
# arity mismatch.
|
|
76
|
+
# The Service method refused its arguments: the call would not bind, or
|
|
77
|
+
# the method raised +ArgumentError+ over what it was given.
|
|
138
78
|
class ServiceArgumentError < ServiceError; end
|
|
139
79
|
|
|
140
|
-
# Raised at a Service
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# unrescued returns it to the guest, which re-raises the exception it
|
|
144
|
-
# raised in the first place. It therefore never reaches the Host App as
|
|
145
|
-
# an invocation outcome and carries no Execution. +klass+ names the
|
|
146
|
-
# guest-side class, which has no host counterpart to rebuild.
|
|
80
|
+
# Raised at a Service's +yield+ when the guest block raised; rescue it as
|
|
81
|
+
# you would the block's own exception. Left unrescued, the guest sees its
|
|
82
|
+
# own exception again, so it never reaches the Host App.
|
|
147
83
|
class BlockError < Error
|
|
148
84
|
include Diagnosable
|
|
149
85
|
end
|
|
150
86
|
|
|
151
|
-
# Raised at a Service
|
|
152
|
-
#
|
|
153
|
-
# block never ran. As BlockError, a Service rescues it at that site and
|
|
154
|
-
# may go on, so it never reaches the Host App as an invocation outcome;
|
|
155
|
-
# unrescued, the dispatch answers as that Service having failed.
|
|
87
|
+
# Raised at a Service's +yield+ when an argument cannot cross to the guest,
|
|
88
|
+
# so the block never ran. Left unrescued, the Service call fails.
|
|
156
89
|
class YieldValueError < Error; end
|
|
157
90
|
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
|
|
91
|
+
# Raised at a Service's +yield+ when the guest serves no block, so the
|
|
92
|
+
# block never ran. Left unrescued, the Service call fails.
|
|
93
|
+
class UnsupportedError < Error; end
|
|
94
|
+
|
|
95
|
+
# One invocation issued more Handles than an id can number (2**31 - 1).
|
|
162
96
|
class HandleExhaustedError < SandboxError; end
|
|
163
97
|
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
# first invocation's snippet replay against a fresh +mrb_state+ (RITE
|
|
167
|
-
# version mismatch or corrupt body). Bytecode that loads cleanly and
|
|
168
|
-
# then raises at top level surfaces as plain +SandboxError+ with the
|
|
169
|
-
# natural mruby class preserved. Inherits from SandboxError so a single
|
|
170
|
-
# +rescue Kobako::SandboxError+ covers both source and bytecode
|
|
171
|
-
# snippet failures while callers wanting bytecode-specific handling
|
|
172
|
-
# can +rescue Kobako::BytecodeError+ directly.
|
|
98
|
+
# A <tt>preload(binary:)</tt> snippet failed to load. Bytecode that loads
|
|
99
|
+
# and then raises is a plain SandboxError.
|
|
173
100
|
class BytecodeError < SandboxError; end
|
|
174
101
|
|
|
175
|
-
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
#
|
|
181
|
-
# +NameError#local_variables+ for the same reason.
|
|
102
|
+
# The Guest Binary does not support a flag the Sandbox set, so no script
|
|
103
|
+
# ran. Retrying without the flag, or on a Guest Binary that supports it,
|
|
104
|
+
# can succeed.
|
|
105
|
+
class UnsupportedFlagError < SandboxError; end
|
|
106
|
+
|
|
107
|
+
# Sandbox#run named no top-level constant.
|
|
182
108
|
class UndefinedEntrypointError < SandboxError
|
|
109
|
+
# The constant asked for, and the ones the preloaded snippets define.
|
|
183
110
|
attr_reader :name, :available
|
|
184
111
|
|
|
185
|
-
def initialize(message, name: nil, available: [], **)
|
|
112
|
+
def initialize(message, name: nil, available: [], **) # :nodoc:
|
|
186
113
|
super(message, **)
|
|
187
114
|
@name = name
|
|
188
115
|
@available = available
|
|
189
116
|
end
|
|
190
117
|
end
|
|
191
118
|
|
|
192
|
-
# Pool
|
|
193
|
-
#
|
|
194
|
-
# held. No Sandbox state is touched — retrying succeeds as soon as a holder
|
|
195
|
-
# returns its Sandbox.
|
|
119
|
+
# Pool#with waited past +checkout_timeout+ with every slot held. Retrying
|
|
120
|
+
# succeeds once a holder returns its Sandbox.
|
|
196
121
|
class PoolTimeoutError < Error; end
|
|
197
122
|
end
|
data/lib/kobako/execution.rb
CHANGED
|
@@ -4,25 +4,20 @@ require_relative "capture"
|
|
|
4
4
|
require_relative "usage"
|
|
5
5
|
|
|
6
6
|
module Kobako
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# Execution on the error's +#execution+, so a rescue reads the captures and
|
|
11
|
-
# usage exactly as a successful caller reads them off the return value. On a
|
|
12
|
-
# failed run +#value+ is +nil+ — only the captures and usage are meaningful.
|
|
7
|
+
# The record of one Sandbox#eval or Sandbox#run: the guest's #value, its
|
|
8
|
+
# output, and its #usage. A failed run raises instead, and the error carries
|
|
9
|
+
# the same record on +execution+, where #value is +nil+.
|
|
13
10
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# one). It holds no reference to the raising error, so the error carries the
|
|
17
|
-
# Execution but not the reverse.
|
|
11
|
+
# #failed? tells the two apart when both #value are +nil+: a script whose
|
|
12
|
+
# last expression was +nil+ versus one that failed.
|
|
18
13
|
class Execution
|
|
19
|
-
# The
|
|
14
|
+
# The guest value the run produced; +nil+ on a failed run.
|
|
20
15
|
attr_reader :value
|
|
21
16
|
|
|
22
|
-
#
|
|
17
|
+
# What the run spent against its caps, as a Usage.
|
|
23
18
|
attr_reader :usage
|
|
24
19
|
|
|
25
|
-
def initialize(value:, usage:, stdout:, stderr:, failed:)
|
|
20
|
+
def initialize(value:, usage:, stdout:, stderr:, failed:) # :nodoc:
|
|
26
21
|
@value = value
|
|
27
22
|
@usage = usage
|
|
28
23
|
@stdout_capture = stdout
|
|
@@ -31,22 +26,22 @@ module Kobako
|
|
|
31
26
|
freeze
|
|
32
27
|
end
|
|
33
28
|
|
|
34
|
-
#
|
|
35
|
-
#
|
|
29
|
+
# Whether the run failed: +false+ on the Execution #eval or #run
|
|
30
|
+
# returned, +true+ on the one a raised error carries.
|
|
36
31
|
def failed? = @failed
|
|
37
32
|
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
33
|
+
# What the guest wrote to stdout, up to +stdout_limit+ bytes: a UTF-8
|
|
34
|
+
# String, or a binary one when the bytes are not valid UTF-8. The text
|
|
35
|
+
# carries no sign it was cut; #stdout_truncated? says so.
|
|
41
36
|
def stdout = @stdout_capture.bytes
|
|
42
37
|
|
|
43
|
-
#
|
|
38
|
+
# What the guest wrote to stderr; see #stdout.
|
|
44
39
|
def stderr = @stderr_capture.bytes
|
|
45
40
|
|
|
46
|
-
#
|
|
41
|
+
# Whether stdout reached +stdout_limit+.
|
|
47
42
|
def stdout_truncated? = @stdout_capture.truncated?
|
|
48
43
|
|
|
49
|
-
#
|
|
44
|
+
# Whether stderr reached +stderr_limit+.
|
|
50
45
|
def stderr_truncated? = @stderr_capture.truncated?
|
|
51
46
|
end
|
|
52
47
|
end
|
data/lib/kobako/extension.rb
CHANGED
|
@@ -1,48 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Kobako
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# methods run in-guest and whose privileged methods dispatch to the
|
|
9
|
-
# backend.
|
|
4
|
+
# Guest source paired with an optional host backend, installed with
|
|
5
|
+
# Sandbox#install: the guest sees one constant whose pure methods run in the
|
|
6
|
+
# guest and whose privileged ones call the backend. Any object answering the
|
|
7
|
+
# same four readers installs the same way.
|
|
10
8
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# * +backend+ — an +Extension::Backend+ or +nil+ for a pure-guest
|
|
20
|
-
# Extension.
|
|
21
|
-
# * +depends_on+ — Symbol names of Extensions that must also be
|
|
22
|
-
# installed, checked for presence at the first invocation.
|
|
23
|
-
#
|
|
24
|
-
# +Kobako::Extension+ is the bundled value type; any object exposing the
|
|
25
|
-
# four readers is equally valid, so a Host App or gem may supply its own.
|
|
9
|
+
# [+name+] A constant-form Symbol: the snippet's backtrace name, and
|
|
10
|
+
# what another Extension's +depends_on+ names.
|
|
11
|
+
# [+source+] The mruby source, a String. A host object with no source
|
|
12
|
+
# is bound with Sandbox#bind instead.
|
|
13
|
+
# [+backend+] A Backend, or +nil+ for an Extension that runs entirely in
|
|
14
|
+
# the guest.
|
|
15
|
+
# [+depends_on+] Names of Extensions that must also be installed, checked
|
|
16
|
+
# at the first invocation.
|
|
26
17
|
class Extension < Data.define(:name, :source, :backend, :depends_on)
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
# +"File"+ or nested +"MyApp::Store"+, spelling the guest constant the
|
|
30
|
-
# idiom routes to) with the source of the bound object, declared by
|
|
31
|
-
# explicit keyword:
|
|
18
|
+
# The host object an Extension's constant at +path+ calls, such as
|
|
19
|
+
# <tt>"MyApp::Store"</tt>, given by keyword:
|
|
32
20
|
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
# * neither — a fillable, defaulting to +Kobako::Unresolved+ until the
|
|
40
|
-
# host supplies the invocation's object.
|
|
21
|
+
# [+object:+] One object for the Sandbox's life.
|
|
22
|
+
# [+provider:+] A callable taking no arguments, called once per invocation
|
|
23
|
+
# for that invocation's object. If it raises, the exception
|
|
24
|
+
# reaches the caller and the guest does not run.
|
|
25
|
+
# [neither] A fillable Service, supplied per invocation through
|
|
26
|
+
# Context#bind.
|
|
41
27
|
#
|
|
42
|
-
# The
|
|
43
|
-
#
|
|
44
|
-
# directly with +object:+. Giving both +object:+ and +provider:+ raises
|
|
45
|
-
# +ArgumentError+.
|
|
28
|
+
# The keyword decides the kind, so a callable object meant as itself is
|
|
29
|
+
# given as +object:+. Giving both raises ArgumentError.
|
|
46
30
|
class Backend < Data.define(:path, :object, :provider)
|
|
47
31
|
def initialize(path:, object: nil, provider: nil)
|
|
48
32
|
if !object.nil? && !provider.nil?
|
|
@@ -54,8 +38,7 @@ module Kobako
|
|
|
54
38
|
end
|
|
55
39
|
end
|
|
56
40
|
|
|
57
|
-
# +backend+ and +depends_on+
|
|
58
|
-
# pure-idiom and single-backend shapes stay terse.
|
|
41
|
+
# +backend+ defaults to none, and +depends_on+ to no other Extension.
|
|
59
42
|
def initialize(name:, source:, backend: nil, depends_on: [])
|
|
60
43
|
super
|
|
61
44
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kobako
|
|
4
|
+
# The Filesystems a Sandbox can mount for the guest's +File+, each
|
|
5
|
+
# answered in Rust by the kind of the same name. Hand one to
|
|
6
|
+
# Sandbox#mount:
|
|
7
|
+
#
|
|
8
|
+
# sandbox.mount("/data", Kobako::Filesystem::Memory.new("config.yml" => yaml))
|
|
9
|
+
#
|
|
10
|
+
# [<tt>Memory.new(files = {}, limit: 64 MiB, inodes:)</tt>]
|
|
11
|
+
# Files held in memory for as long as the object lives, seeded from a
|
|
12
|
+
# Hash of path to contents. Every invocation shares them.
|
|
13
|
+
# [<tt>Temporary.new(limit: 64 MiB, inodes:)</tt>]
|
|
14
|
+
# Files held in memory that every invocation starts empty.
|
|
15
|
+
# [<tt>Directory.new(path)</tt>]
|
|
16
|
+
# A host directory and everything beneath it. Nothing outside it is
|
|
17
|
+
# reachable, a symbolic link pointing out included.
|
|
18
|
+
# [<tt>Overlay.new.layer(filesystem, access: :read)</tt>]
|
|
19
|
+
# Layers stacked into one view, the first added on top, each +:read+
|
|
20
|
+
# or +:read_write+ on its own. A change lands in a writable Layer, and
|
|
21
|
+
# a read-only one stays as it was. Each +layer+ answers a new Overlay.
|
|
22
|
+
#
|
|
23
|
+
# A Memory or Temporary is bounded as a tmpfs mount is: its files hold
|
|
24
|
+
# at most +limit+ bytes together, and it holds at most +inodes+ files and
|
|
25
|
+
# directories, its root among them; +nil+ lifts either bound. Without
|
|
26
|
+
# +inodes+ it holds one for each 4 KiB of +limit+, and the root. Past
|
|
27
|
+
# either, the guest raises Errno::ENOSPC.
|
|
28
|
+
#
|
|
29
|
+
# Any object answering +open+ and +stat+ mounts too, as a Filesystem
|
|
30
|
+
# written in Ruby; its methods take the names of +File+, +Dir+, and +IO+.
|
|
31
|
+
# Its +stat+ answers +directory?+, +size+, and +ino+, an +ino+ naming one
|
|
32
|
+
# directory for as long as the guest holds it open. Its names match
|
|
33
|
+
# exactly, as a mount's block judges them as given. A name may hold a
|
|
34
|
+
# <tt>\\</tt>, which the guest's paths never separate on, so one joined
|
|
35
|
+
# to a Windows host path is the Filesystem's to refuse.
|
|
36
|
+
module Filesystem
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/kobako/handle.rb
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Kobako
|
|
4
|
-
# A
|
|
5
|
-
#
|
|
6
|
-
# direction. Host App code cannot construct one: +.new+ is private and
|
|
7
|
-
# +#with+ removed, so no Handle is ever made from a caller-chosen id.
|
|
8
|
-
# The guest's +Kobako::Handle+ shares only the name.
|
|
4
|
+
# A guest-held reference to a host object, named by id. Host code cannot
|
|
5
|
+
# construct one, so no Handle is ever made from a caller-chosen id.
|
|
9
6
|
class Handle < Data.define(:id)
|
|
10
|
-
#
|
|
7
|
+
# The smallest id; 0 never names a Handle.
|
|
11
8
|
MIN_ID = 1
|
|
12
|
-
# The positive half of a signed 32-bit integer
|
|
13
|
-
#
|
|
9
|
+
# The largest id, the positive half of a signed 32-bit integer; an
|
|
10
|
+
# invocation needing more raises HandleExhaustedError.
|
|
14
11
|
MAX_ID = 0x7fff_ffff
|
|
15
12
|
|
|
16
|
-
def initialize(id:)
|
|
13
|
+
def initialize(id:) # :nodoc:
|
|
17
14
|
raise ArgumentError, "Handle id must be Integer" unless id.is_a?(Integer)
|
|
18
15
|
raise ArgumentError, "Handle id #{id} out of range [#{MIN_ID}, #{MAX_ID}]" unless id.between?(MIN_ID, MAX_ID)
|
|
19
16
|
|
|
@@ -23,9 +20,7 @@ module Kobako
|
|
|
23
20
|
private_class_method :new
|
|
24
21
|
undef_method :with
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
# allocator; it runs the same id checks +.new+ would.
|
|
28
|
-
def self.restore(id)
|
|
23
|
+
def self.restore(id) # :nodoc:
|
|
29
24
|
allocate.tap { |handle| handle.send(:initialize, id: id) }
|
|
30
25
|
end
|
|
31
26
|
end
|