Skip to content

Commit 6a8e581

Browse files
authored
fix(wasm): upgrade Wasmtime to 43.0.1 and restore CI (#2224)
* fix(wasm): upgrade wasmtime to 43.0.1 * chore(wasm): align wasmparser with wasmtime deps
1 parent 26e5e4c commit 6a8e581

8 files changed

Lines changed: 495 additions & 386 deletions

File tree

Cargo.lock

Lines changed: 425 additions & 324 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ open = "5"
147147
pgvector = { version = "0.4", features = ["postgres"], optional = true }
148148

149149
# WASM sandbox for untrusted tool execution
150-
wasmtime = { version = "28", features = ["component-model"] }
151-
wasmtime-wasi = "28" # WASI support for component model
152-
wasmparser = "0.220" # WASM binary parsing for validation
150+
wasmtime = { version = "43.0.1", features = ["component-model"] }
151+
wasmtime-wasi = "43.0.1" # WASI support for component model
152+
wasmparser = "0.245.1" # WASM binary parsing for validation
153153

154154
# Cryptography for secrets management
155155
aes-gcm = "0.10"

src/channels/wasm/wrapper.rs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use tokio_tungstenite::tungstenite::protocol::Message as WebsocketMessage;
4040
use uuid::Uuid;
4141
use wasmtime::Store;
4242
use wasmtime::component::Linker;
43-
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
43+
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
4444

4545
use crate::channels::wasm::capabilities::ChannelCapabilities;
4646
use crate::channels::wasm::error::WasmChannelError;
@@ -74,7 +74,6 @@ const TELEGRAM_TEST_API_BASE_ENV: &str = "IRONCLAW_TEST_TELEGRAM_API_BASE_URL";
7474
wasmtime::component::bindgen!({
7575
path: "wit/channel.wit",
7676
world: "sandboxed-channel",
77-
async: false,
7877
with: {
7978
// Use our own store data type
8079
},
@@ -268,12 +267,11 @@ impl ChannelStoreData {
268267

269268
// Implement WasiView to provide WASI context and resource table
270269
impl WasiView for ChannelStoreData {
271-
fn ctx(&mut self) -> &mut WasiCtx {
272-
&mut self.wasi
273-
}
274-
275-
fn table(&mut self) -> &mut ResourceTable {
276-
&mut self.table
270+
fn ctx(&mut self) -> WasiCtxView<'_> {
271+
WasiCtxView {
272+
ctx: &mut self.wasi,
273+
table: &mut self.table,
274+
}
277275
}
278276
}
279277

@@ -1129,14 +1127,16 @@ impl WasmChannel {
11291127
/// to properly register all host functions with correct component model signatures.
11301128
fn add_host_functions(linker: &mut Linker<ChannelStoreData>) -> Result<(), WasmChannelError> {
11311129
// Add WASI support (required by the component adapter)
1132-
wasmtime_wasi::add_to_linker_sync(linker).map_err(|e| {
1130+
wasmtime_wasi::p2::add_to_linker_sync(linker).map_err(|e| {
11331131
WasmChannelError::Config(format!("Failed to add WASI functions: {}", e))
11341132
})?;
11351133

11361134
// Use the generated add_to_linker function from bindgen for our custom interface
1137-
near::agent::channel_host::add_to_linker(linker, |state| state).map_err(|e| {
1138-
WasmChannelError::Config(format!("Failed to add host functions: {}", e))
1139-
})?;
1135+
SandboxedChannel::add_to_linker::<_, wasmtime::component::HasSelf<_>>(
1136+
linker,
1137+
|state: &mut ChannelStoreData| state,
1138+
)
1139+
.map_err(|e| WasmChannelError::Config(format!("Failed to add host functions: {}", e)))?;
11401140

11411141
Ok(())
11421142
}
@@ -1445,8 +1445,12 @@ impl WasmChannel {
14451445
}
14461446

14471447
/// Map WASM execution errors to our error types.
1448-
fn map_wasm_error(e: anyhow::Error, name: &str, fuel_limit: u64) -> WasmChannelError {
1449-
let error_str = e.to_string();
1448+
fn map_wasm_error(
1449+
e: impl Into<anyhow::Error>,
1450+
name: &str,
1451+
fuel_limit: u64,
1452+
) -> WasmChannelError {
1453+
let error_str = e.into().to_string();
14501454
if error_str.contains("out of fuel") {
14511455
WasmChannelError::FuelExhausted {
14521456
name: name.to_string(),

src/tools/builder/validation.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl WasmValidator {
166166
Ok(exp) => {
167167
let kind = match exp.kind {
168168
wasmparser::ExternalKind::Func => ExportKind::Function,
169+
wasmparser::ExternalKind::FuncExact => ExportKind::Function,
169170
wasmparser::ExternalKind::Memory => ExportKind::Memory,
170171
wasmparser::ExternalKind::Table => ExportKind::Table,
171172
wasmparser::ExternalKind::Global => ExportKind::Global,
@@ -186,11 +187,12 @@ impl WasmValidator {
186187
}
187188
}
188189
Ok(wasmparser::Payload::ImportSection(reader)) => {
189-
for import in reader {
190+
for import in reader.into_imports() {
190191
match import {
191192
Ok(imp) => {
192193
let kind = match imp.ty {
193194
wasmparser::TypeRef::Func(_) => ImportKind::Function,
195+
wasmparser::TypeRef::FuncExact(_) => ImportKind::Function,
194196
wasmparser::TypeRef::Memory(_) => ImportKind::Memory,
195197
wasmparser::TypeRef::Table(_) => ImportKind::Table,
196198
wasmparser::TypeRef::Global(_) => ImportKind::Global,

src/tools/wasm/limits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ impl ResourceLimiter for WasmResourceLimiter {
102102
current: usize,
103103
desired: usize,
104104
_maximum: Option<usize>,
105-
) -> anyhow::Result<bool> {
105+
) -> Result<bool, wasmtime::Error> {
106106
let desired_u64 = desired as u64;
107107

108108
if desired_u64 > self.memory_limit {
@@ -130,7 +130,7 @@ impl ResourceLimiter for WasmResourceLimiter {
130130
current: usize,
131131
desired: usize,
132132
_maximum: Option<usize>,
133-
) -> anyhow::Result<bool> {
133+
) -> Result<bool, wasmtime::Error> {
134134
// Allow reasonable table growth
135135
if desired > 10_000 {
136136
tracing::warn!(

src/tools/wasm/runtime.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::sync::Arc;
99
use std::time::Duration;
1010

1111
use tokio::sync::RwLock;
12-
use wasmtime::{Config, Engine, OptLevel};
12+
use wasmtime::{Cache, Config, Engine, OptLevel};
1313

1414
use crate::tools::wasm::error::WasmError;
1515
use crate::tools::wasm::limits::{FuelConfig, ResourceLimits};
@@ -18,15 +18,17 @@ use crate::tools::wasm::limits::{FuelConfig, ResourceLimits};
1818
/// which causes any store with an expired epoch deadline to trap.
1919
pub const EPOCH_TICK_INTERVAL: Duration = Duration::from_millis(500);
2020

21-
/// Enable wasmtime's persistent compilation cache for a [`Config`].
21+
/// Enable Wasmtime's persistent compilation cache for a [`Config`].
2222
///
23-
/// On Unix, this delegates to `cache_config_load_default()` which uses a
24-
/// shared cache directory. On Windows, each engine gets its own subdirectory
25-
/// (keyed by `label`) to avoid OS error 33 (`ERROR_LOCK_VIOLATION`) when
26-
/// multiple engines memory-map files in the same cache directory. See #448.
23+
/// If `explicit_dir` is `Some`, this writes a small cache TOML file pointing
24+
/// at that directory and loads it with [`Cache::from_file`].
2725
///
28-
/// If `explicit_dir` is `Some`, it is used as the cache directory on all
29-
/// platforms, bypassing the default.
26+
/// If `explicit_dir` is `None`, we load Wasmtime's default cache configuration
27+
/// by calling `Cache::from_file(None)`.
28+
///
29+
/// On Windows, the caller typically passes an engine-specific directory keyed
30+
/// by `label` to avoid OS error 33 (`ERROR_LOCK_VIOLATION`) when multiple
31+
/// engines memory-map files in the same cache directory. See #448.
3032
pub fn enable_compilation_cache(
3133
wasmtime_config: &mut Config,
3234
label: &str,
@@ -60,11 +62,11 @@ pub fn enable_compilation_cache(
6062
.replace('"', "\\\"");
6163
let toml_content = format!("[cache]\nenabled = true\ndirectory = \"{}\"\n", escaped);
6264
std::fs::write(&toml_path, toml_content)?;
63-
wasmtime_config.cache_config_load(&toml_path)?;
65+
wasmtime_config.cache(Some(Cache::from_file(Some(&toml_path))?));
6466
Ok(())
6567
}
6668
None => {
67-
wasmtime_config.cache_config_load_default()?;
69+
wasmtime_config.cache(Some(Cache::from_file(None)?));
6870
Ok(())
6971
}
7072
}

src/tools/wasm/wrapper.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::time::{Duration, Instant};
1414
use async_trait::async_trait;
1515
use wasmtime::Store;
1616
use wasmtime::component::Linker;
17-
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
17+
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
1818

1919
use crate::auth::resolve_secret_for_runtime;
2020
use crate::context::JobContext;
@@ -42,7 +42,6 @@ use ironclaw_safety::LeakDetector;
4242
wasmtime::component::bindgen!({
4343
path: "wit/tool.wit",
4444
world: "sandboxed-tool",
45-
async: false,
4645
with: {},
4746
});
4847

@@ -266,12 +265,11 @@ impl StoreData {
266265
// Provide WASI context for the WASM component.
267266
// Required because tools are compiled with wasm32-wasip2 target.
268267
impl WasiView for StoreData {
269-
fn ctx(&mut self) -> &mut WasiCtx {
270-
&mut self.wasi
271-
}
272-
273-
fn table(&mut self) -> &mut ResourceTable {
274-
&mut self.table
268+
fn ctx(&mut self) -> WasiCtxView<'_> {
269+
WasiCtxView {
270+
ctx: &mut self.wasi,
271+
table: &mut self.table,
272+
}
275273
}
276274
}
277275

@@ -976,12 +974,15 @@ impl WasmToolWrapper {
976974
/// `near:agent/host` namespace.
977975
fn add_host_functions(linker: &mut Linker<StoreData>) -> Result<(), WasmError> {
978976
// Add WASI support (required by components built with wasm32-wasip2)
979-
wasmtime_wasi::add_to_linker_sync(linker)
977+
wasmtime_wasi::p2::add_to_linker_sync(linker)
980978
.map_err(|e| WasmError::ConfigError(format!("Failed to add WASI functions: {}", e)))?;
981979

982980
// Add our custom host interface using the generated add_to_linker
983-
near::agent::host::add_to_linker(linker, |state| state)
984-
.map_err(|e| WasmError::ConfigError(format!("Failed to add host functions: {}", e)))?;
981+
SandboxedTool::add_to_linker::<_, wasmtime::component::HasSelf<_>>(
982+
linker,
983+
|state: &mut StoreData| state,
984+
)
985+
.map_err(|e| WasmError::ConfigError(format!("Failed to add host functions: {}", e)))?;
985986

986987
Ok(())
987988
}

tests/wit_compat.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
1414
use std::path::{Path, PathBuf};
1515

16-
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
16+
use wasmtime_wasi::{ResourceTable, WasiCtx, WasiCtxBuilder, WasiCtxView, WasiView};
1717

1818
/// Minimal store data that satisfies WasiView for component instantiation.
1919
struct TestStoreData {
@@ -31,12 +31,11 @@ impl TestStoreData {
3131
}
3232

3333
impl WasiView for TestStoreData {
34-
fn ctx(&mut self) -> &mut WasiCtx {
35-
&mut self.wasi
36-
}
37-
38-
fn table(&mut self) -> &mut ResourceTable {
39-
&mut self.table
34+
fn ctx(&mut self) -> WasiCtxView<'_> {
35+
WasiCtxView {
36+
ctx: &mut self.wasi,
37+
table: &mut self.table,
38+
}
4039
}
4140
}
4241

@@ -167,30 +166,30 @@ fn compile_component(
167166
fn stub_shared_host_functions(
168167
host: &mut wasmtime::component::LinkerInstance<'_, TestStoreData>,
169168
) -> Result<(), String> {
170-
host.func_new("log", |_ctx, _args, _results| Ok(()))
169+
host.func_new("log", |_ctx, _ty, _args, _results| Ok(()))
171170
.map_err(|e| format!("stub 'log': {e}"))?;
172171

173-
host.func_new("now-millis", |_ctx, _args, results| {
172+
host.func_new("now-millis", |_ctx, _ty, _args, results| {
174173
results[0] = wasmtime::component::Val::U64(0);
175174
Ok(())
176175
})
177176
.map_err(|e| format!("stub 'now-millis': {e}"))?;
178177

179-
host.func_new("workspace-read", |_ctx, _args, results| {
178+
host.func_new("workspace-read", |_ctx, _ty, _args, results| {
180179
results[0] = wasmtime::component::Val::Option(None);
181180
Ok(())
182181
})
183182
.map_err(|e| format!("stub 'workspace-read': {e}"))?;
184183

185-
host.func_new("http-request", |_ctx, _args, results| {
184+
host.func_new("http-request", |_ctx, _ty, _args, results| {
186185
results[0] = wasmtime::component::Val::Result(Err(Some(Box::new(
187186
wasmtime::component::Val::String("stub".into()),
188187
))));
189188
Ok(())
190189
})
191190
.map_err(|e| format!("stub 'http-request': {e}"))?;
192191

193-
host.func_new("secret-exists", |_ctx, _args, results| {
192+
host.func_new("secret-exists", |_ctx, _ty, _args, results| {
194193
results[0] = wasmtime::component::Val::Bool(false);
195194
Ok(())
196195
})
@@ -209,7 +208,7 @@ fn instantiate_tool_component(
209208

210209
let mut linker: Linker<TestStoreData> = Linker::new(engine);
211210

212-
wasmtime_wasi::add_to_linker_sync(&mut linker)
211+
wasmtime_wasi::p2::add_to_linker_sync(&mut linker)
213212
.map_err(|e| format!("WASI linker failed: {e}"))?;
214213

215214
// If the WIT added/removed/renamed a function, stub registration
@@ -221,7 +220,7 @@ fn instantiate_tool_component(
221220
if let Ok(mut host) = root.instance(interface) {
222221
stub_shared_host_functions(&mut host)?;
223222

224-
host.func_new("tool-invoke", |_ctx, _args, results| {
223+
host.func_new("tool-invoke", |_ctx, _ty, _args, results| {
225224
results[0] = wasmtime::component::Val::Result(Err(Some(Box::new(
226225
wasmtime::component::Val::String("stub".into()),
227226
))));
@@ -249,7 +248,7 @@ fn instantiate_channel_component(
249248

250249
let mut linker: Linker<TestStoreData> = Linker::new(engine);
251250

252-
wasmtime_wasi::add_to_linker_sync(&mut linker)
251+
wasmtime_wasi::p2::add_to_linker_sync(&mut linker)
253252
.map_err(|e| format!("WASI linker failed: {e}"))?;
254253

255254
// Register stubs for both versioned (0.3.0+) and unversioned (pre-0.3.0) interface
@@ -261,30 +260,30 @@ fn instantiate_channel_component(
261260
) -> Result<(), String> {
262261
stub_shared_host_functions(host)?;
263262

264-
host.func_new("store-attachment-data", |_ctx, _args, results| {
263+
host.func_new("store-attachment-data", |_ctx, _ty, _args, results| {
265264
results[0] = wasmtime::component::Val::Result(Ok(None));
266265
Ok(())
267266
})
268267
.map_err(|e| format!("stub 'store-attachment-data': {e}"))?;
269268

270-
host.func_new("emit-message", |_ctx, _args, _results| Ok(()))
269+
host.func_new("emit-message", |_ctx, _ty, _args, _results| Ok(()))
271270
.map_err(|e| format!("stub 'emit-message': {e}"))?;
272271

273-
host.func_new("workspace-write", |_ctx, _args, results| {
272+
host.func_new("workspace-write", |_ctx, _ty, _args, results| {
274273
results[0] = wasmtime::component::Val::Result(Ok(None));
275274
Ok(())
276275
})
277276
.map_err(|e| format!("stub 'workspace-write': {e}"))?;
278277

279-
host.func_new("pairing-upsert-request", |_ctx, _args, results| {
278+
host.func_new("pairing-upsert-request", |_ctx, _ty, _args, results| {
280279
results[0] = wasmtime::component::Val::Result(Err(Some(Box::new(
281280
wasmtime::component::Val::String("stub".into()),
282281
))));
283282
Ok(())
284283
})
285284
.map_err(|e| format!("stub 'pairing-upsert-request': {e}"))?;
286285

287-
host.func_new("pairing-resolve-identity", |_ctx, _args, results| {
286+
host.func_new("pairing-resolve-identity", |_ctx, _ty, _args, results| {
288287
// Test stub: unknown sender — returns Ok(option::none).
289288
results[0] = wasmtime::component::Val::Result(Ok(Some(Box::new(
290289
wasmtime::component::Val::Option(None),
@@ -293,7 +292,7 @@ fn instantiate_channel_component(
293292
})
294293
.map_err(|e| format!("stub 'pairing-resolve-identity': {e}"))?;
295294

296-
host.func_new("pairing-read-allow-from", |_ctx, _args, results| {
295+
host.func_new("pairing-read-allow-from", |_ctx, _ty, _args, results| {
297296
results[0] = wasmtime::component::Val::Result(Ok(Some(Box::new(
298297
wasmtime::component::Val::List(vec![]),
299298
))));

0 commit comments

Comments
 (0)