Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ libc = { version = "0.2.112", default-features = true }
file-per-thread-logger = "0.2.0"
tokio = { version = "1.43.0", features = [ "rt", "time" ] }
hyper = "1.0.1"
http = "1.0.0"
http = "1.4.0"
http-body = "1.0.0"
http-body-util = "0.1.0"
bytes = { version = "1.4", default-features = false }
Expand Down
26 changes: 26 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
## 36.0.6

Released 2026-02-24.

### Changed

* Wasmtime's implementation of WASI now has the ability to limit resource
consumption on behalf of the guest, such as host-allocated memory. This means
that some behaviors previously allowed by Wasmtime can now disallowed, such as
transferring excessive data from the guest to the host. Additionally calls to
`wasi:random/random.get-random-bytes`, for example, can have limits in place
to avoid allocating too much memory on the host. To preserve
backwards-compatible behavior these limits are NOT set by default. Embedders
must opt-in to configuring these knobs as appropriate for their embeddings.
For more information on this see the related security advisory with further
details on knobs added and what behaviors can be restricted.
[GHSA-852m-cvvp-9p4w](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-852m-cvvp-9p4w)

### Fixed

* Panics when adding too many headers to a `wasi:http/types.fields` has been
resolved
[GHSA-243v-98vx-264h](https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-243v-98vx-264h)

--------------------------------------------------------------------------------

## 36.0.5

Released 2026-01-26.
Expand Down
12 changes: 12 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,18 @@ wasmtime_option_group! {
/// Preset data for the In-Memory provider of WASI key-value API.
#[serde(skip)]
pub keyvalue_in_memory_data: Vec<KeyValuePair>,
/// Maximum resources the guest is allowed to create simultaneously.
pub max_resources: Option<usize>,
/// Fuel to use for all hostcalls to limit guest<->host data transfer.
pub hostcall_fuel: Option<usize>,
/// Maximum value, in bytes, for a wasi-random 0.2
/// `get{,-insecure}-random-bytes` `len` parameter. Calls with a value
/// exceeding this limit will trap.
pub max_random_size: Option<u64>,
/// Maximum value, in bytes, for the contents of a wasi-http 0.2
/// `fields` resource (aka `headers` and `trailers`). `fields` methods
/// which cause the contents to exceed this size limit will trap.
pub max_http_fields_size: Option<usize>,
}

enum Wasi {
Expand Down
3 changes: 2 additions & 1 deletion crates/test-programs/artifacts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ impl Artifacts {
s if s.starts_with("p3_http_") => "p3_http",
s if s.starts_with("p3_api_") => "p3_api",
s if s.starts_with("p3_") => "p3",

// If you're reading this because you hit this panic, either add
// it to a test suite above or add a new "suite". The purpose of
// the categorization above is to have a static assertion that
Expand Down Expand Up @@ -256,7 +257,7 @@ impl Artifacts {
// Prevent stray files for now that we don't understand.
Some(_) => panic!("unknown file extension on {path:?}"),

None => unreachable!(),
None => unreachable!("no extension in path {path:?}"),
}
}
}
Expand Down
65 changes: 65 additions & 0 deletions crates/test-programs/src/bin/api_proxy.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use test_programs::wasi::http::types::{
Headers, IncomingRequest, Method, OutgoingBody, OutgoingResponse, ResponseOutparam,
};
Expand Down Expand Up @@ -29,6 +30,16 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T {

return;
}
(Method::Get, Some(p)) if p.starts_with("/modify_fields/") => {
let r = modify_fields_handler(request);
response_for(r, outparam);
return;
}
(Method::Get, Some(p)) if p.starts_with("/new_fields/") => {
let r = new_fields_handler(request);
response_for(r, outparam);
return;
}

_ => {}
}
Expand Down Expand Up @@ -69,10 +80,64 @@ impl test_programs::proxy::exports::wasi::http::incoming_handler::Guest for T {
}
}

fn response_for(r: Result<()>, outparam: ResponseOutparam) {
let resp = OutgoingResponse::new(Headers::new());
resp.set_status_code(if r.is_ok() { 200 } else { 500 })
.unwrap();
let body = resp.body().expect("outgoing response");
ResponseOutparam::set(outparam, Ok(resp));
let _ = body.write().and_then(|out| {
let _ = out.blocking_write_and_flush(format!("{r:?}").as_bytes());
drop(out);
Ok(())
});
let _ = OutgoingBody::finish(body, None);
}

// Technically this should not be here for a proxy, but given the current
// framework for tests it's required since this file is built as a `bin`
fn main() {}

fn test_filesystem() {
assert!(std::fs::File::open(".").is_err());
}

fn add_bytes_to_headers(headers: Headers, size: usize) {
if size == 0 {
return;
} else if size < 10 {
headers.append("k", &b"abcdefghi"[0..size - 1]).unwrap()
} else {
for chunk in 0..(size / 10) {
let k = format!("g{chunk:04}");
let mut v = format!("h{chunk:04}");
if chunk == 0 {
for _ in 0..(size % 10) {
v.push('#');
}
}
headers.append(k.as_str(), v.as_bytes()).unwrap()
}
}
}

fn modify_fields_handler(request: IncomingRequest) -> Result<()> {
let path = request.path_with_query().unwrap();
let rest = path.trim_start_matches("/modify_fields/");
let added_field_bytes: usize = rest
.parse()
.context("expect remainder of url to parse as number")?;
add_bytes_to_headers(request.headers().clone(), added_field_bytes);

Ok(())
}
fn new_fields_handler(request: IncomingRequest) -> Result<()> {
let path = request.path_with_query().unwrap();
let rest = path.trim_start_matches("/new_fields/");
let added_field_bytes: usize = rest
.parse()
.context("expect remainder of url to parse as number")?;
add_bytes_to_headers(Headers::new(), added_field_bytes);

Ok(())
}
33 changes: 33 additions & 0 deletions crates/test-programs/src/bin/cli_http_headers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
fn main() {
let fields = wasip2::http::types::Fields::new();

match std::env::args().nth(1).as_deref() {
Some("append") => {
for i in 0.. {
if fields.append(&format!("a{i}"), b"a").is_err() {
break;
}
}
}
Some("append-empty") => {
for i in 0.. {
if fields.append(&format!("a{i}"), b"").is_err() {
break;
}
}
}
Some("append-same") => loop {
if fields.append("a", b"b").is_err() {
break;
}
},
Some("append-same-empty") => loop {
if fields.append("a", b"").is_err() {
break;
}
},
other => panic!("unknown test {other:?}"),
}

unreachable!();
}
7 changes: 7 additions & 0 deletions crates/test-programs/src/bin/cli_many_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use test_programs::wasi::clocks::monotonic_clock::subscribe_duration;

fn main() {
loop {
std::mem::forget(subscribe_duration(1_000_000));
}
}
6 changes: 6 additions & 0 deletions crates/test-programs/src/bin/cli_max_resources.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
fn main() {
let mut buf = Vec::new();
for _ in 0..100 {
buf.push(wasip2::clocks::monotonic_clock::subscribe_duration(0));
}
}
118 changes: 118 additions & 0 deletions crates/test-programs/src/bin/cli_p1_hostcall_fuel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use std::ptr;

fn main() {
big_poll();
big_string();
big_iovecs();
}

fn big_string() {
let mut s = String::new();
for _ in 0..10_000 {
s.push_str("hello world");
}
let dir_fd = test_programs::preview1::open_scratch_directory(".").unwrap();
assert_eq!(
unsafe { wasip1::path_create_directory(dir_fd, &s) },
Err(wasip1::ERRNO_NOMEM)
);
}

fn big_iovecs() {
let mut iovs = Vec::new();
let mut ciovs = Vec::new();
for _ in 0..10_000 {
iovs.push(wasip1::Iovec {
buf: ptr::null_mut(),
buf_len: 0,
});
ciovs.push(wasip1::Ciovec {
buf: ptr::null(),
buf_len: 0,
});
}
let dir_fd = test_programs::preview1::open_scratch_directory(".").unwrap();
let fd = unsafe {
wasip1::path_open(
dir_fd,
0,
"hi",
wasip1::OFLAGS_CREAT,
wasip1::RIGHTS_FD_WRITE | wasip1::RIGHTS_FD_READ,
0,
0,
)
.unwrap()
};

unsafe {
assert_eq!(wasip1::fd_write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_read(fd, &iovs), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_pwrite(fd, &ciovs, 0), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_pread(fd, &iovs, 0), Err(wasip1::ERRNO_NOMEM));
}

ciovs.truncate(1);
iovs.truncate(1);
iovs.push(wasip1::Iovec {
buf: ptr::null_mut(),
buf_len: 10_000,
});
ciovs.push(wasip1::Ciovec {
buf: ptr::null(),
buf_len: 10_000,
});
unsafe {
assert_eq!(wasip1::fd_write(fd, &ciovs), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_read(fd, &iovs), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_pwrite(fd, &ciovs, 0), Err(wasip1::ERRNO_NOMEM));
assert_eq!(wasip1::fd_pread(fd, &iovs, 0), Err(wasip1::ERRNO_NOMEM));
}
}

fn big_poll() {
let mut huge_poll = Vec::new();
let mut huge_events = Vec::new();
for _ in 0..10_000 {
huge_poll.push(subscribe_timeout(0));
huge_events.push(empty_event());
}
let err = unsafe {
wasip1::poll_oneoff(
huge_poll.as_ptr(),
huge_events.as_mut_ptr(),
huge_poll.len(),
)
.unwrap_err()
};
assert_eq!(err, wasip1::ERRNO_NOMEM);

fn subscribe_timeout(timeout: u64) -> wasip1::Subscription {
wasip1::Subscription {
userdata: 0,
u: wasip1::SubscriptionU {
tag: wasip1::EVENTTYPE_CLOCK.raw(),
u: wasip1::SubscriptionUU {
clock: wasip1::SubscriptionClock {
id: wasip1::CLOCKID_MONOTONIC,
timeout,
precision: 0,
flags: 0,
},
},
},
}
}

fn empty_event() -> wasip1::Event {
wasip1::Event {
error: wasip1::ERRNO_SUCCESS,
fd_readwrite: wasip1::EventFdReadwrite {
nbytes: 0,
flags: 0,
},
type_: wasip1::EVENTTYPE_CLOCK,
userdata: 0,
}
}
}
Loading
Loading