From f2b4eb0abf4134ea6bddb3f9bae5fa1b52d63f52 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 22 Nov 2023 16:52:13 -0700 Subject: [PATCH 1/2] add `adapter_{open|close}_badfd` exports to Preview 1 adapter This is to be used by `wasi-libc` to reserve file descriptors for its own use (e.g. for sockets), ensuring that any attempt to pass them directly to Preview 1 functions will consistently return an error. See https://github.com/WebAssembly/wasi-libc/issues/447 for further details. Signed-off-by: Joel Dice --- .../src/descriptors.rs | 8 +++-- .../src/lib.rs | 32 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/crates/wasi-preview1-component-adapter/src/descriptors.rs b/crates/wasi-preview1-component-adapter/src/descriptors.rs index 0c9cab019f18..23e0a45d43ee 100644 --- a/crates/wasi-preview1-component-adapter/src/descriptors.rs +++ b/crates/wasi-preview1-component-adapter/src/descriptors.rs @@ -20,6 +20,8 @@ pub enum Descriptor { /// Input and/or output wasi-streams, along with stream metadata. Streams(Streams), + + Bad, } /// Input and/or output wasi-streams, along with a stream type that @@ -358,7 +360,7 @@ impl Descriptors { ) -> Result<&mut Streams, Errno> { match self.get_mut(fd)? { Descriptor::Streams(streams) => Ok(streams), - Descriptor::Closed(_) => Err(error), + Descriptor::Closed(_) | Descriptor::Bad => Err(error), } } @@ -420,14 +422,14 @@ impl Descriptors { pub fn get_read_stream(&self, fd: Fd) -> Result<&InputStream, Errno> { match self.get(fd)? { Descriptor::Streams(streams) => streams.get_read_stream(), - Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), + Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), } } pub fn get_write_stream(&self, fd: Fd) -> Result<&OutputStream, Errno> { match self.get(fd)? { Descriptor::Streams(streams) => streams.get_write_stream(), - Descriptor::Closed(_) => Err(wasi::ERRNO_BADF), + Descriptor::Closed(_) | Descriptor::Bad => Err(wasi::ERRNO_BADF), } } } diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs index b19f8c948399..59561f3ef150 100644 --- a/crates/wasi-preview1-component-adapter/src/lib.rs +++ b/crates/wasi-preview1-component-adapter/src/lib.rs @@ -145,6 +145,26 @@ impl TrappingUnwrap for Result { } } +/// Allocate a file descriptor which will generate an `ERRNO_BADF` if passed to +/// any WASI Preview 1 function implemented by this adapter. +/// +/// This is intended for use by `wasi-libc` during its incremental transition +/// from WASI Preview 1 to Preview 2. It will use this function to reserve +/// descriptors for its own use, valid only for use with libc functions. +#[no_mangle] +pub unsafe extern "C" fn adapter_open_badfd(fd: *mut u32) -> bool { + State::with(|state| { + *fd = state.descriptors_mut().open(Descriptor::Bad)?; + Ok(()) + }) == wasi::ERRNO_SUCCESS +} + +/// Close a descriptor previously opened using `adapter_open_badfd`. +#[no_mangle] +pub unsafe extern "C" fn adapter_close_badfd(fd: u32) -> bool { + State::with(|state| state.descriptors_mut().close(fd)) == wasi::ERRNO_SUCCESS +} + #[no_mangle] pub unsafe extern "C" fn reset_adapter_state() { let state = get_state_ptr(); @@ -525,6 +545,10 @@ pub unsafe extern "C" fn fd_allocate(fd: Fd, _offset: Filesize, _len: Filesize) #[no_mangle] pub unsafe extern "C" fn fd_close(fd: Fd) -> Errno { State::with(|state| { + if let Descriptor::Bad = state.descriptors().get(fd)? { + return Err(wasi::ERRNO_BADF); + } + // If there's a dirent cache entry for this file descriptor then drop // it since the descriptor is being closed and future calls to // `fd_readdir` should return an error. @@ -669,7 +693,7 @@ pub unsafe extern "C" fn fd_fdstat_get(fd: Fd, stat: *mut Fdstat) -> Errno { }); Ok(()) } - Descriptor::Closed(_) => Err(ERRNO_BADF), + Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF), } }) } @@ -716,7 +740,7 @@ pub unsafe extern "C" fn fd_fdstat_set_rights( let ds = state.descriptors(); match ds.get(fd)? { Descriptor::Streams(..) => Ok(()), - Descriptor::Closed(..) => Err(wasi::ERRNO_BADF), + Descriptor::Closed(..) | Descriptor::Bad => Err(wasi::ERRNO_BADF), } }) } @@ -1028,7 +1052,7 @@ pub unsafe extern "C" fn fd_read( forget(data); Ok(()) } - Descriptor::Closed(_) => Err(ERRNO_BADF), + Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF), } }) } @@ -1446,7 +1470,7 @@ pub unsafe extern "C" fn fd_write( *nwritten = nbytes; Ok(()) } - Descriptor::Closed(_) => Err(ERRNO_BADF), + Descriptor::Closed(_) | Descriptor::Bad => Err(ERRNO_BADF), } }) } From 117154b58b711ad2d4b711d58c45a4556f27c959 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 8 Dec 2023 14:53:22 -0700 Subject: [PATCH 2/2] add `preview2_adapter_badfd` test Signed-off-by: Joel Dice --- .../src/bin/preview2_adapter_badfd.rs | 46 +++++++++++++++++++ crates/wasi/tests/all/async_.rs | 4 ++ crates/wasi/tests/all/sync.rs | 4 ++ 3 files changed, 54 insertions(+) create mode 100644 crates/test-programs/src/bin/preview2_adapter_badfd.rs diff --git a/crates/test-programs/src/bin/preview2_adapter_badfd.rs b/crates/test-programs/src/bin/preview2_adapter_badfd.rs new file mode 100644 index 000000000000..7baa33717225 --- /dev/null +++ b/crates/test-programs/src/bin/preview2_adapter_badfd.rs @@ -0,0 +1,46 @@ +fn main() { + #[link(wasm_import_module = "wasi_snapshot_preview1")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "adapter_open_badfd")] + fn adapter_open_badfd(fd: *mut u32) -> bool; + + #[cfg_attr(target_arch = "wasm32", link_name = "adapter_close_badfd")] + fn adapter_close_badfd(fd: u32) -> bool; + } + + unsafe { + let mut fd = 0; + assert!(adapter_open_badfd(&mut fd)); + + assert_eq!(wasi::fd_close(fd), Err(wasi::ERRNO_BADF)); + + assert_eq!(wasi::fd_fdstat_get(fd).map(drop), Err(wasi::ERRNO_BADF)); + + assert_eq!(wasi::fd_fdstat_set_rights(fd, 0, 0), Err(wasi::ERRNO_BADF)); + + let mut buffer = [0_u8; 1]; + assert_eq!( + wasi::fd_read( + fd, + &[wasi::Iovec { + buf: buffer.as_mut_ptr(), + buf_len: 1 + }] + ), + Err(wasi::ERRNO_BADF) + ); + + assert_eq!( + wasi::fd_write( + fd, + &[wasi::Ciovec { + buf: buffer.as_ptr(), + buf_len: 1 + }] + ), + Err(wasi::ERRNO_BADF) + ); + + assert!(adapter_close_badfd(fd)); + } +} diff --git a/crates/wasi/tests/all/async_.rs b/crates/wasi/tests/all/async_.rs index 49ba0c52a96b..f47997e0e6ca 100644 --- a/crates/wasi/tests/all/async_.rs +++ b/crates/wasi/tests/all/async_.rs @@ -354,3 +354,7 @@ async fn preview2_stream_pollable_traps() { "resource has children" ) } +#[test_log::test(tokio::test(flavor = "multi_thread"))] +async fn preview2_adapter_badfd() { + run(PREVIEW2_ADAPTER_BADFD_COMPONENT, false).await.unwrap() +} diff --git a/crates/wasi/tests/all/sync.rs b/crates/wasi/tests/all/sync.rs index c005ac35ea7f..ce0c2e161937 100644 --- a/crates/wasi/tests/all/sync.rs +++ b/crates/wasi/tests/all/sync.rs @@ -293,3 +293,7 @@ fn preview2_stream_pollable_traps() { "resource has children" ) } +#[test_log::test] +fn preview2_adapter_badfd() { + run(PREVIEW2_ADAPTER_BADFD_COMPONENT, false).unwrap() +}