Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 12 additions & 0 deletions crates/cli-flags/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,18 @@ wasmtime_option_group! {
pub keyvalue_in_memory_data: Vec<KeyValuePair>,
/// Enable support for WASIp3 APIs.
pub p3: Option<bool>,
/// 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
15 changes: 10 additions & 5 deletions crates/test-programs/artifacts/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,26 @@ impl Artifacts {
// Bucket, based on the name of the test, into a "kind" which
// generates a `foreach_*` macro below.
let kind = match test.name.as_str() {
s if s.starts_with("p1_cli_")
|| s.starts_with("p2_cli_")
|| s.starts_with("p3_cli_") =>
{
"cli"
}
s if s.starts_with("p1_") => "p1",
s if s.starts_with("p2_http_") => "p2_http",
s if s.starts_with("p2_cli_") => "p2_cli",
s if s.starts_with("p2_api_") => "p2_api",
s if s.starts_with("p2_") => "p2",
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",
s if s.starts_with("nn_") => "nn",
s if s.starts_with("piped_") => "piped",
s if s.starts_with("dwarf_") => "dwarf",
s if s.starts_with("config_") => "config",
s if s.starts_with("keyvalue_") => "keyvalue",
s if s.starts_with("tls_") => "tls",
s if s.starts_with("async_") => "async",
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",
s if s.starts_with("fuzz_") => "fuzz",
// 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
Expand Down Expand Up @@ -283,7 +288,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
118 changes: 118 additions & 0 deletions crates/test-programs/src/bin/p1_cli_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,
}
}
}
98 changes: 98 additions & 0 deletions crates/test-programs/src/bin/p1_sleep_quickly_but_lots.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use std::process;
use test_programs::preview1::open_scratch_directory;

fn main() {
let arg = std::env::args().nth(1).unwrap();
let dir_fd = match open_scratch_directory(&arg) {
Ok(dir_fd) => dir_fd,
Err(err) => {
eprintln!("{err}");
process::exit(1)
}
};

// Wait for just one timeout (maybe hitting a fast path)
let subs = [subscribe_timeout(0)];
for _ in 0..1000 {
let mut events = [empty_event()];
unsafe {
wasip1::poll_oneoff(subs.as_ptr(), events.as_mut_ptr(), 1).unwrap();
}
}

// Wait for two timeouts
let subs = [subscribe_timeout(0), subscribe_timeout(0)];
for _ in 0..1000 {
let mut events = [empty_event(), empty_event()];
unsafe {
wasip1::poll_oneoff(subs.as_ptr(), events.as_mut_ptr(), 2).unwrap();
}
}

let file_fd = unsafe {
wasip1::path_open(
dir_fd,
0,
"hello.txt",
wasip1::OFLAGS_CREAT,
wasip1::RIGHTS_FD_WRITE | wasip1::RIGHTS_FD_READ,
0,
0,
)
.expect("creating a file for writing")
};

// Wait for a timeout fd operations
let subs = [
subscribe_timeout(0),
subscribe_fd(wasip1::EVENTTYPE_FD_READ, file_fd),
subscribe_fd(wasip1::EVENTTYPE_FD_WRITE, file_fd),
];
for _ in 0..1000 {
let mut events = [empty_event(), empty_event(), empty_event()];
unsafe {
wasip1::poll_oneoff(subs.as_ptr(), events.as_mut_ptr(), 3).unwrap();
}
}
}

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 subscribe_fd(ty: wasip1::Eventtype, file_descriptor: wasip1::Fd) -> wasip1::Subscription {
wasip1::Subscription {
userdata: 0,
u: wasip1::SubscriptionU {
tag: ty.raw(),
u: wasip1::SubscriptionUU {
fd_read: wasip1::SubscriptionFdReadwrite { file_descriptor },
},
},
}
}

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,
}
}
65 changes: 65 additions & 0 deletions crates/test-programs/src/bin/p2_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(())
}
Loading