Repository: bytecodealliance/wasmtime
Pull request #7433 (2023-10-31 23:51:27)

fuzzgen: Allow restricting generated opcodes with env var
👋 Hey,

This PR allows users to pass a environment variable to fuzzgen that restricts the instructions that it can generate. Passing `FUZZGEN_ALLOWED_OPS=ineg,ushr` restricts fuzzgen to generate only those two instructions.

Using this allows me to get better fuzzing throughput when testing a specific rule or lowering.

abrown: Drive-by: I'd agree with the documentation piece @elliottt mentioned and would go a bit further to note that this environment variable approach (which I also used for the `differential` target) means the corpus is invalid if it was generated in a different environment &mdash; I have forgotten this fact before (e.g., when sending in a reproducer).

Patch:
diff --git a/cranelift/fuzzgen/src/function_generator.rs b/cranelift/fuzzgen/src/function_generator.rs
index 651b784b2..38cdc0c05 100644
--- a/cranelift/fuzzgen/src/function_generator.rs
+++ b/cranelift/fuzzgen/src/function_generator.rs
@@ -22,6 +22,7 @@ use cranelift::prelude::{
 use once_cell::sync::Lazy;
 use std::collections::HashMap;
 use std::ops::RangeInclusive;
+use std::str::FromStr;
 use target_lexicon::{Architecture, Triple};
 
 type BlockSignature = Vec<Type>;
@@ -797,6 +798,17 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
         F32X4, F64X2, // SIMD Floats
     ];
 
+    // When this env variable is passed, we only generate instructions for the opcodes listed in
+    // the comma-separated list. This is useful for debugging, as it allows us to focus on a few
+    // specific opcodes.
+    let allowed_opcodes = std::env::var("FUZZGEN_ALLOWED_OPS").ok().map(|s| {
+        s.split(',')
+            .map(|s| s.trim())
+            .filter(|s| !s.is_empty())
+            .map(|s| Opcode::from_str(s).expect("Unrecoginzed opcode"))
+            .collect::<Vec<_>>()
+    });
+
     Opcode::all()
         .iter()
         .filter(|op| {
@@ -1048,6 +1060,11 @@ static OPCODE_SIGNATURES: Lazy<Vec<OpcodeSignature>> = Lazy::new(|| {
                 (Opcode::FcvtFromSint, &[I32X4], &[F64X2]),
             )
         })
+        .filter(|(op, ..)| {
+            allowed_opcodes
+                .as_ref()
+                .map_or(true, |opcodes| opcodes.contains(op))
+        })
         .collect()
 });
 
diff --git a/crates/wasi-http/src/body.rs b/crates/wasi-http/src/body.rs
index 00a3cc50a..25d29a539 100644
--- a/crates/wasi-http/src/body.rs
+++ b/crates/wasi-http/src/body.rs
@@ -13,14 +13,14 @@ use wasmtime_wasi::preview2::{
     self, AbortOnDropJoinHandle, HostInputStream, HostOutputStream, StreamError, Subscribe,
 };
 
-pub type HyperIncomingBody = BoxBody<Bytes, anyhow::Error>;
+pub type HyperIncomingBody = BoxBody<Bytes, types::Error>;
 
 /// Holds onto the things needed to construct a [`HostIncomingBody`] until we are ready to build
 /// one. The HostIncomingBody spawns a task that starts consuming the incoming body, and we don't
 /// want to do that unless the user asks to consume the body.
 pub struct HostIncomingBodyBuilder {
     pub body: HyperIncomingBody,
-    pub worker: Option<Arc<AbortOnDropJoinHandle<anyhow::Result<()>>>>,
+    pub worker: Option<Arc<AbortOnDropJoinHandle<Result<(), types::Error>>>>,
     pub between_bytes_timeout: Duration,
 }
 
@@ -118,7 +118,7 @@ impl HostIncomingBodyBuilder {
 pub struct HostIncomingBody {
     pub worker: AbortOnDropJoinHandle<()>,
     pub stream: Option<HostIncomingBodyStream>,
-    pub trailers: oneshot::Receiver<Result<hyper::HeaderMap, anyhow::Error>>,
+    pub trailers: oneshot::Receiver<Result<hyper::HeaderMap, types::Error>>,
 }
 
 impl HostIncomingBody {
@@ -132,13 +132,13 @@ impl HostIncomingBody {
 
 pub struct HostIncomingBodyStream {
     pub open: bool,
-    pub receiver: mpsc::Receiver<Result<Bytes, anyhow::Error>>,
+    pub receiver: mpsc::Receiver<Result<Bytes, types::Error>>,
     pub buffer: Bytes,
     pub error: Option<anyhow::Error>,
 }
 
 impl HostIncomingBodyStream {
-    fn new(receiver: mpsc::Receiver<Result<Bytes, anyhow::Error>>) -> Self {
+    fn new(receiver: mpsc::Receiver<Result<Bytes, types::Error>>) -> Self {
         Self {
             open: true,
             receiver,
@@ -180,7 +180,7 @@ impl HostInputStream for HostIncomingBodyStream {
 
             Ok(Err(e)) => {
                 self.open = false;
-                return Err(StreamError::LastOperationFailed(e));
+                return Err(StreamError::LastOperationFailed(e.into()));
             }
 
             Err(TryRecvError::Empty) => {
@@ -210,7 +210,7 @@ impl Subscribe for HostIncomingBodyStream {
             Some(Ok(bytes)) => self.buffer = bytes,
 
             Some(Err(e)) => {
-                self.error = Some(e);
+                self.error = Some(e.into());
                 self.open = false;
             }
 
@@ -225,7 +225,7 @@ pub struct HostFutureTrailers {
 }
 
 pub enum HostFutureTrailersState {
-    Waiting(oneshot::Receiver<Result<hyper::HeaderMap, anyhow::Error>>),
+    Waiting(oneshot::Receiver<Result<hyper::HeaderMap, types::Error>>),
     Done(Result<Option<FieldMap>, types::Error>),
 }
 
@@ -251,7 +251,7 @@ impl Subscribe for HostFutureTrailers {
     }
 }
 
-pub type HyperOutgoingBody = BoxBody<Bytes, anyhow::Error>;
+pub type HyperOutgoingBody = BoxBody<Bytes, types::Error>;
 
 pub enum FinishMessage {
     Finished,
@@ -276,7 +276,7 @@ impl HostOutgoingBody {
         }
         impl Body for BodyImpl {
             type Data = Bytes;
-            type Error = anyhow::Error;
+            type Error = types::Error;
             fn poll_frame(
                 mut self: Pin<&mut Self>,
                 cx: &mut Context<'_>,
@@ -299,7 +299,7 @@ impl HostOutgoingBody {
                                         Poll::Ready(Some(Ok(Frame::trailers(trailers))))
                                     }
                                     FinishMessage::Abort => Poll::Ready(Some(Err(
-                                        anyhow::anyhow!("response corrupted"),
+                                        types::Error::ProtocolError("response corrupted".into()),
                                     ))),
                                 },
                                 Poll::Ready(Err(RecvError { .. })) => Poll::Ready(None),
diff --git a/crates/wasi-http/src/http_impl.rs b/crates/wasi-http/src/http_impl.rs
index 6e172dba5..47ff08930 100644
--- a/crates/wasi-http/src/http_impl.rs
+++ b/crates/wasi-http/src/http_impl.rs
@@ -82,11 +82,9 @@ impl<T: WasiHttpView> outgoing_handler::Host for T {
             builder = builder.header(k, v);
         }
 
-        let body = req.body.unwrap_or_else(|| {
-            Empty::<Bytes>::new()
-                .map_err(|_| anyhow::anyhow!("empty error"))
-                .boxed()
-        });
+        let body = req
+            .body
+            .unwrap_or_else(|| Empty::<Bytes>::new().map_err(|_| unreachable!()).boxed());
 
         let request = builder.body(body).map_err(types::http_protocol_error)?;
 
diff --git a/crates/wasi-http/src/lib.rs b/crates/wasi-http/src/lib.rs
index d791f20eb..936c48a1f 100644
--- a/crates/wasi-http/src/lib.rs
+++ b/crates/wasi-http/src/lib.rs
@@ -66,12 +66,6 @@ impl From<std::io::Error> for crate::bindings::http::types::Error {
     }
 }
 
-impl From<http::Error> for crate::bindings::http::types::Error {
-    fn from(err: http::Error) -> Self {
-        Self::InvalidUrl(err.to_string())
-    }
-}
-
 impl From<hyper::Error> for crate::bindings::http::types::Error {
     fn from(err: hyper::Error) -> Self {
         let message = err.message().to_string();
diff --git a/crates/wasi-http/src/types.rs b/crates/wasi-http/src/types.rs
index 36070d5e4..dc41a30c6 100644
--- a/crates/wasi-http/src/types.rs
+++ b/crates/wasi-http/src/types.rs
@@ -5,7 +5,6 @@ use crate::{
     bindings::http::types::{self, Method, Scheme},
     body::{HostIncomingBodyBuilder, HyperIncomingBody, HyperOutgoingBody},
 };
-use anyhow::Context;
 use http_body_util::BodyExt;
 use hyper::header::HeaderName;
 use std::any::Any;
@@ -86,118 +85,129 @@ pub fn default_send_request(
     }: OutgoingRequest,
 ) -> wasmtime::Result<Resource<HostFutureIncomingResponse>> {
     let handle = preview2::spawn(async move {
-        let tcp_stream = TcpStream::connect(authority.clone())
-            .await
-            .map_err(invalid_url)?;
-
-        let (mut sender, worker) = if use_tls {
-            #[cfg(any(target_arch = "riscv64", target_arch = "s390x"))]
-            {
-                anyhow::bail!(crate::bindings::http::types::Error::UnexpectedError(
-                    "unsupported architecture for SSL".to_string(),
-                ));
-            }
+        let resp = handler(
+            authority,
+            use_tls,
+            connect_timeout,
+            first_byte_timeout,
+            request,
+            between_bytes_timeout,
+        )
+        .await;
+        Ok(resp)
+    });
 
-            #[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))]
-            {
-                use tokio_rustls::rustls::OwnedTrustAnchor;
-
-                // derived from https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/client/src/main.rs
-                let mut root_cert_store = rustls::RootCertStore::empty();
-                root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(
-                    |ta| {
-                        OwnedTrustAnchor::from_subject_spki_name_constraints(
-                            ta.subject,
-                            ta.spki,
-                            ta.name_constraints,
-                        )
-                    },
-                ));
-                let config = rustls::ClientConfig::builder()
-                    .with_safe_defaults()
-                    .with_root_certificates(root_cert_store)
-                    .with_no_client_auth();
-                let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config));
-                let mut parts = authority.split(":");
-                let host = parts.next().unwrap_or(&authority);
-                let domain = rustls::ServerName::try_from(host)?;
-                let stream = connector.connect(domain, tcp_stream).await.map_err(|e| {
-                    crate::bindings::http::types::Error::ProtocolError(e.to_string())
-                })?;
-
-                let (sender, conn) = timeout(
-                    connect_timeout,
-                    hyper::client::conn::http1::handshake(stream),
+    let fut = view.table().push(HostFutureIncomingResponse::new(handle))?;
+
+    Ok(fut)
+}
+
+async fn handler(
+    authority: String,
+    use_tls: bool,
+    connect_timeout: Duration,
+    first_byte_timeout: Duration,
+    request: http::Request<HyperOutgoingBody>,
+    between_bytes_timeout: Duration,
+) -> Result<IncomingResponseInternal, types::Error> {
+    let tcp_stream = TcpStream::connect(authority.clone())
+        .await
+        .map_err(invalid_url)?;
+
+    let (mut sender, worker) = if use_tls {
+        #[cfg(any(target_arch = "riscv64", target_arch = "s390x"))]
+        {
+            return Err(crate::bindings::http::types::Error::UnexpectedError(
+                "unsupported architecture for SSL".to_string(),
+            ));
+        }
+
+        #[cfg(not(any(target_arch = "riscv64", target_arch = "s390x")))]
+        {
+            use tokio_rustls::rustls::OwnedTrustAnchor;
+
+            // derived from https://github.com/tokio-rs/tls/blob/master/tokio-rustls/examples/client/src/main.rs
+            let mut root_cert_store = rustls::RootCertStore::empty();
+            root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| {
+                OwnedTrustAnchor::from_subject_spki_name_constraints(
+                    ta.subject,
+                    ta.spki,
+                    ta.name_constraints,
                 )
+            }));
+            let config = rustls::ClientConfig::builder()
+                .with_safe_defaults()
+                .with_root_certificates(root_cert_store)
+                .with_no_client_auth();
+            let connector = tokio_rustls::TlsConnector::from(std::sync::Arc::new(config));
+            let mut parts = authority.split(":");
+            let host = parts.next().unwrap_or(&authority);
+            let domain = rustls::ServerName::try_from(host)?;
+            let stream = connector
+                .connect(domain, tcp_stream)
                 .await
-                .map_err(|_| timeout_error("connection"))??;
-
-                let worker = preview2::spawn(async move {
-                    conn.await.context("hyper connection failed")?;
-                    Ok::<_, anyhow::Error>(())
-                });
+                .map_err(|e| crate::bindings::http::types::Error::ProtocolError(e.to_string()))?;
 
-                (sender, worker)
-            }
-        } else {
             let (sender, conn) = timeout(
                 connect_timeout,
-                // TODO: we should plumb the builder through the http context, and use it here
-                hyper::client::conn::http1::handshake(tcp_stream),
+                hyper::client::conn::http1::handshake(stream),
             )
             .await
             .map_err(|_| timeout_error("connection"))??;
 
             let worker = preview2::spawn(async move {
-                conn.await.context("hyper connection failed")?;
-                Ok::<_, anyhow::Error>(())
+                let _ = conn.await;
+                Ok::<_, types::Error>(())
             });
 
             (sender, worker)
-        };
-
-        let resp = timeout(first_byte_timeout, sender.send_request(request))
-            .await
-            .map_err(|_| timeout_error("first byte"))?
-            .map_err(hyper_protocol_error)?
-            .map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed());
-
-        Ok(IncomingResponseInternal {
-            resp,
-            worker: Arc::new(worker),
-            between_bytes_timeout,
-        })
-    });
-
-    let fut = view.table().push(HostFutureIncomingResponse::new(handle))?;
-
-    Ok(fut)
+        }
+    } else {
+        let (sender, conn) = timeout(
+            connect_timeout,
+            // TODO: we should plumb the builder through the http context, and use it here
+            hyper::client::conn::http1::handshake(tcp_stream),
+        )
+        .await
+        .map_err(|_| timeout_error("connection"))??;
+
+        let worker = preview2::spawn(async move {
+            conn.await?;
+            Ok::<_, types::Error>(())
+        });
+
+        (sender, worker)
+    };
+
+    let resp = timeout(first_byte_timeout, sender.send_request(request))
+        .await
+        .map_err(|_| timeout_error("first byte"))?
+        .map_err(hyper_protocol_error)?
+        .map(|body| body.map_err(|e| e.into()).boxed());
+
+    Ok(IncomingResponseInternal {
+        resp,
+        worker: Arc::new(worker),
+        between_bytes_timeout,
+    })
 }
 
-pub fn timeout_error(kind: &str) -> anyhow::Error {
-    anyhow::anyhow!(crate::bindings::http::types::Error::TimeoutError(format!(
-        "{kind} timed out"
-    )))
+pub fn timeout_error(kind: &str) -> types::Error {
+    types::Error::TimeoutError(format!("{kind} timed out"))
 }
 
-pub fn http_protocol_error(e: http::Error) -> anyhow::Error {
-    anyhow::anyhow!(crate::bindings::http::types::Error::ProtocolError(
-        e.to_string()
-    ))
+pub fn http_protocol_error(e: http::Error) -> types::Error {
+    types::Error::ProtocolError(e.to_string())
 }
 
-pub fn hyper_protocol_error(e: hyper::Error) -> anyhow::Error {
-    anyhow::anyhow!(crate::bindings::http::types::Error::ProtocolError(
-        e.to_string()
-    ))
+pub fn hyper_protocol_error(e: hyper::Error) -> types::Error {
+    types::Error::ProtocolError(e.to_string())
 }
 
-fn invalid_url(e: std::io::Error) -> anyhow::Error {
+fn invalid_url(e: std::io::Error) -> types::Error {
     // TODO: DNS errors show up as a Custom io error, what subset of errors should we consider for
     // InvalidUrl here?
-    anyhow::anyhow!(crate::bindings::http::types::Error::InvalidUrl(
-        e.to_string()
-    ))
+    types::Error::InvalidUrl(e.to_string())
 }
 
 impl From<http::Method> for types::Method {
@@ -275,7 +285,7 @@ pub struct HostIncomingResponse {
     pub status: u16,
     pub headers: FieldMap,
     pub body: Option<HostIncomingBodyBuilder>,
-    pub worker: Arc<AbortOnDropJoinHandle<anyhow::Result<()>>>,
+    pub worker: Arc<AbortOnDropJoinHandle<Result<(), types::Error>>>,
 }
 
 pub struct HostOutgoingResponse {
@@ -300,7 +310,7 @@ impl TryFrom<HostOutgoingResponse> for hyper::Response<HyperOutgoingBody> {
             Some(body) => builder.body(body),
             None => builder.body(
                 Empty::<bytes::Bytes>::new()
-                    .map_err(|_| anyhow::anyhow!("empty error"))
+                    .map_err(|_| unreachable!())
                     .boxed(),
             ),
         }
@@ -326,15 +336,16 @@ pub enum HostFields {
 
 pub struct IncomingResponseInternal {
     pub resp: hyper::Response<HyperIncomingBody>,
-    pub worker: Arc<AbortOnDropJoinHandle<anyhow::Result<()>>>,
+    pub worker: Arc<AbortOnDropJoinHandle<Result<(), types::Error>>>,
     pub between_bytes_timeout: std::time::Duration,
 }
 
-type FutureIncomingResponseHandle = AbortOnDropJoinHandle<anyhow::Result<IncomingResponseInternal>>;
+type FutureIncomingResponseHandle =
+    AbortOnDropJoinHandle<anyhow::Result<Result<IncomingResponseInternal, types::Error>>>;
 
 pub enum HostFutureIncomingResponse {
     Pending(FutureIncomingResponseHandle),
-    Ready(anyhow::Result<IncomingResponseInternal>),
+    Ready(anyhow::Result<Result<IncomingResponseInternal, types::Error>>),
     Consumed,
 }
 
@@ -347,7 +358,7 @@ impl HostFutureIncomingResponse {
         matches!(self, Self::Ready(_))
     }
 
-    pub fn unwrap_ready(self) -> anyhow::Result<IncomingResponseInternal> {
+    pub fn unwrap_ready(self) -> anyhow::Result<Result<IncomingResponseInternal, types::Error>> {
         match self {
             Self::Ready(res) => res,
             Self::Pending(_) | Self::Consumed => {
diff --git a/crates/wasi-http/src/types_impl.rs b/crates/wasi-http/src/types_impl.rs
index 422ecde2c..2f7612af1 100644
--- a/crates/wasi-http/src/types_impl.rs
+++ b/crates/wasi-http/src/types_impl.rs
@@ -734,7 +734,8 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFutureIncomingResponse f
                     return Ok(Some(Ok(Err(e))));
                 }
 
-                Ok(resp) => resp,
+                Ok(Ok(resp)) => resp,
+                Ok(Err(e)) => return Ok(Some(Ok(Err(e)))),
             };
 
         let (parts, body) = resp.resp.into_parts();
@@ -752,6 +753,7 @@ impl<T: WasiHttpView> crate::bindings::http::types::HostFutureIncomingResponse f
 
         Ok(Some(Ok(Ok(resp))))
     }
+
     fn subscribe(
         &mut self,
         id: Resource<HostFutureIncomingResponse>,
diff --git a/crates/wasi/src/preview2/stdio.rs b/crates/wasi/src/preview2/stdio.rs
index 1ad802302..29ec38f08 100644
--- a/crates/wasi/src/preview2/stdio.rs
+++ b/crates/wasi/src/preview2/stdio.rs
@@ -3,8 +3,11 @@ use crate::preview2::bindings::cli::{
     terminal_stdout,
 };
 use crate::preview2::bindings::io::streams;
-use crate::preview2::pipe::{self, AsyncWriteStream};
-use crate::preview2::{HostInputStream, HostOutputStream, WasiView};
+use crate::preview2::pipe;
+use crate::preview2::{
+    HostInputStream, HostOutputStream, StreamError, StreamResult, Subscribe, WasiView,
+};
+use bytes::Bytes;
 use std::io::IsTerminal;
 use wasmtime::component::Resource;
 
@@ -56,12 +59,6 @@ impl StdinStream for pipe::ClosedInputStream {
 mod worker_thread_stdin;
 pub use self::worker_thread_stdin::{stdin, Stdin};
 
-// blocking-write-and-flush must accept 4k. It doesn't seem likely that we need to
-// buffer more than that to implement a wrapper on the host process's stdio. If users
-// really need more, they can write their own implementation using AsyncWriteStream
-// and tokio's stdout/err.
-const STDIO_BUFFER_SIZE: usize = 4096;
-
 /// Similar to [`StdinStream`], except for output.
 pub trait StdoutStream: Send + Sync {
     /// Returns a fresh new stream which can write to this output stream.
@@ -112,6 +109,10 @@ impl StdoutStream for pipe::ClosedOutputStream {
     }
 }
 
+/// This implementation will yield output streams that block on writes, as they
+/// inherit the implementation directly from the rust std library. A different
+/// implementation of [`StdoutStream`] will be necessary if truly async output
+/// streams are required.
 pub struct Stdout;
 
 pub fn stdout() -> Stdout {
@@ -120,10 +121,7 @@ pub fn stdout() -> Stdout {
 
 impl StdoutStream for Stdout {
     fn stream(&self) -> Box<dyn HostOutputStream> {
-        Box::new(AsyncWriteStream::new(
-            STDIO_BUFFER_SIZE,
-            tokio::io::stdout(),
-        ))
+        Box::new(OutputStream::Stdout)
     }
 
     fn isatty(&self) -> bool {
@@ -131,6 +129,10 @@ impl StdoutStream for Stdout {
     }
 }
 
+/// This implementation will yield output streams that block on writes, as they
+/// inherit the implementation directly from the rust std library. A different
+/// implementation of [`StdoutStream`] will be necessary if truly async output
+/// streams are required.
 pub struct Stderr;
 
 pub fn stderr() -> Stderr {
@@ -139,10 +141,7 @@ pub fn stderr() -> Stderr {
 
 impl StdoutStream for Stderr {
     fn stream(&self) -> Box<dyn HostOutputStream> {
-        Box::new(AsyncWriteStream::new(
-            STDIO_BUFFER_SIZE,
-            tokio::io::stderr(),
-        ))
+        Box::new(OutputStream::Stderr)
     }
 
     fn isatty(&self) -> bool {
@@ -150,6 +149,40 @@ impl StdoutStream for Stderr {
     }
 }
 
+enum OutputStream {
+    Stdout,
+    Stderr,
+}
+
+impl HostOutputStream for OutputStream {
+    fn write(&mut self, bytes: Bytes) -> StreamResult<()> {
+        use std::io::Write;
+        match self {
+            OutputStream::Stdout => std::io::stdout().write_all(&bytes),
+            OutputStream::Stderr => std::io::stderr().write_all(&bytes),
+        }
+        .map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
+    }
+
+    fn flush(&mut self) -> StreamResult<()> {
+        use std::io::Write;
+        match self {
+            OutputStream::Stdout => std::io::stdout().flush(),
+            OutputStream::Stderr => std::io::stderr().flush(),
+        }
+        .map_err(|e| StreamError::LastOperationFailed(anyhow::anyhow!(e)))
+    }
+
+    fn check_write(&mut self) -> StreamResult<usize> {
+        Ok(1024 * 1024)
+    }
+}
+
+#[async_trait::async_trait]
+impl Subscribe for OutputStream {
+    async fn ready(&mut self) {}
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum IsATTY {
     Yes,
diff --git a/fuzz/README.md b/fuzz/README.md
index f52c2c71b..5efabdcf3 100644
--- a/fuzz/README.md
+++ b/fuzz/README.md
@@ -88,3 +88,15 @@ following steps to reproduce it locally:
    to print the configuration and WebAssembly input used by the test case (see
    uses of  `log_wasm` in the `wasmtime-fuzzing` crate).
 
+## Target specific options
+
+### `cranelift-fuzzgen`
+
+Fuzzgen supports passing the `FUZZGEN_ALLOWED_OPS` environment variable, which when available restricts the instructions that it will generate.
+
+Running `FUZZGEN_ALLOWED_OPS=ineg,ishl cargo fuzz run cranelift-fuzzgen` will run fuzzgen but only generate `ineg` or `ishl` opcodes.
+
+### `cranelift-icache`
+
+The icache target also uses the fuzzgen library, thus also supports the `FUZZGEN_ALLOWED_OPS` enviornment variable as described in the `cranelift-fuzzgen` section above.
+
diff --git a/src/commands/serve.rs b/src/commands/serve.rs
index 762f73c8a..31276a1aa 100644
--- a/src/commands/serve.rs
+++ b/src/commands/serve.rs
@@ -363,9 +363,9 @@ impl hyper::service::Service<Request> for ProxyHandler {
 
             let mut store = inner.cmd.new_store(&inner.engine, req_id)?;
 
-            let req = store.data_mut().new_incoming_request(
-                req.map(|body| body.map_err(|e| anyhow::anyhow!(e)).boxed()),
-            )?;
+            let req = store
+                .data_mut()
+                .new_incoming_request(req.map(|body| body.map_err(|e| e.into()).boxed()))?;
 
             let out = store.data_mut().new_response_outparam(sender)?;
 


Test patch:
diff --git a/crates/wasi-http/tests/all/main.rs b/crates/wasi-http/tests/all/main.rs
index 9f8386b5e..3683213df 100644
--- a/crates/wasi-http/tests/all/main.rs
+++ b/crates/wasi-http/tests/all/main.rs
@@ -265,13 +265,16 @@ async fn do_wasi_http_hash_all(override_send_request: bool) -> Result<()> {
                       between_bytes_timeout,
                       ..
                   }| {
-                Ok(view.table().push(HostFutureIncomingResponse::Ready(
-                    handle(request.into_parts().0).map(|resp| IncomingResponseInternal {
+                let response = handle(request.into_parts().0).map(|resp| {
+                    Ok(IncomingResponseInternal {
                         resp,
                         worker: Arc::new(preview2::spawn(future::ready(Ok(())))),
                         between_bytes_timeout,
-                    }),
-                ))?)
+                    })
+                });
+                Ok(view
+                    .table()
+                    .push(HostFutureIncomingResponse::Ready(response))?)
             },
         ) as RequestSender)
     } else {
@@ -447,7 +450,7 @@ async fn do_wasi_http_echo(uri: &str, url_header: Option<&str>) -> Result<()> {
 
     let request = request.body(BoxBody::new(StreamBody::new(stream::iter(
         body.chunks(16 * 1024)
-            .map(|chunk| Ok::<_, anyhow::Error>(Frame::data(Bytes::copy_from_slice(chunk))))
+            .map(|chunk| Ok::<_, Error>(Frame::data(Bytes::copy_from_slice(chunk))))
             .collect::<Vec<_>>(),
     ))))?;