Repository: bytecodealliance/wasmtime
Pull request #9393 (2024-10-08 01:48:12)

Increase alignment of empty mmap pointers
This commit updates platform implementations of `Mmap::new_empty()` to use a shared helper to create a pointer with a higher alignment than one. This fixes a debug assert tripping in niche configurations of the pooling allocator where if no virtual memory is given and table lazy init is disabled then a module could trip a debug-only assert about a slice being created from a raw pointer that is not properly aligned. The fix here is to use a helper that starts with an over-aligned empty slice which is then casted down to a byte slice.

<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->



Patch:
diff --git a/ci/vendor-wit.sh b/ci/vendor-wit.sh
index 55ffdf2a4..1e83d720b 100755
--- a/ci/vendor-wit.sh
+++ b/ci/vendor-wit.sh
@@ -37,22 +37,22 @@ make_vendor() {
 cache_dir=$(mktemp -d)
 
 make_vendor "wasi" "
-  cli@v0.2.1
-  clocks@v0.2.1
-  filesystem@v0.2.1
-  io@v0.2.1
-  random@v0.2.1
-  sockets@v0.2.1
+  cli@v0.2.2
+  clocks@v0.2.2
+  filesystem@v0.2.2
+  io@v0.2.2
+  random@v0.2.2
+  sockets@v0.2.2
 "
 
 make_vendor "wasi-http" "
-  cli@v0.2.1
-  clocks@v0.2.1
-  filesystem@v0.2.1
-  io@v0.2.1
-  random@v0.2.1
-  sockets@v0.2.1
-  http@v0.2.1
+  cli@v0.2.2
+  clocks@v0.2.2
+  filesystem@v0.2.2
+  io@v0.2.2
+  random@v0.2.2
+  sockets@v0.2.2
+  http@v0.2.2
 "
 
 make_vendor "wasi-runtime-config" "runtime-config@c667fe6"
diff --git a/crates/wasi-http/wit/deps/cli/command.wit b/crates/wasi-http/wit/deps/cli/command.wit
index dc064a3cd..cc7a352c2 100644
--- a/crates/wasi-http/wit/deps/cli/command.wit
+++ b/crates/wasi-http/wit/deps/cli/command.wit
@@ -1,4 +1,4 @@
-package wasi:cli@0.2.1;
+package wasi:cli@0.2.2;
 
 @since(version = 0.2.0)
 world command {
diff --git a/crates/wasi-http/wit/deps/cli/imports.wit b/crates/wasi-http/wit/deps/cli/imports.wit
index b8339d3b2..ebd7ba173 100644
--- a/crates/wasi-http/wit/deps/cli/imports.wit
+++ b/crates/wasi-http/wit/deps/cli/imports.wit
@@ -1,17 +1,17 @@
-package wasi:cli@0.2.1;
+package wasi:cli@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
   @since(version = 0.2.0)
-  include wasi:clocks/imports@0.2.1;
+  include wasi:clocks/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:filesystem/imports@0.2.1;
+  include wasi:filesystem/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:sockets/imports@0.2.1;
+  include wasi:sockets/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:random/imports@0.2.1;
+  include wasi:random/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:io/imports@0.2.1;
+  include wasi:io/imports@0.2.2;
 
   @since(version = 0.2.0)
   import environment;
diff --git a/crates/wasi-http/wit/deps/cli/stdio.wit b/crates/wasi-http/wit/deps/cli/stdio.wit
index d1d26eb61..860313eea 100644
--- a/crates/wasi-http/wit/deps/cli/stdio.wit
+++ b/crates/wasi-http/wit/deps/cli/stdio.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface stdin {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{input-stream};
+  use wasi:io/streams@0.2.2.{input-stream};
 
   @since(version = 0.2.0)
   get-stdin: func() -> input-stream;
@@ -10,7 +10,7 @@ interface stdin {
 @since(version = 0.2.0)
 interface stdout {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{output-stream};
+  use wasi:io/streams@0.2.2.{output-stream};
 
   @since(version = 0.2.0)
   get-stdout: func() -> output-stream;
@@ -19,7 +19,7 @@ interface stdout {
 @since(version = 0.2.0)
 interface stderr {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{output-stream};
+  use wasi:io/streams@0.2.2.{output-stream};
 
   @since(version = 0.2.0)
   get-stderr: func() -> output-stream;
diff --git a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit
index 3c24840c9..233cace4c 100644
--- a/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit
+++ b/crates/wasi-http/wit/deps/clocks/monotonic-clock.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 /// WASI Monotonic Clock is a clock API intended to let users measure elapsed
 /// time.
 ///
@@ -10,7 +10,7 @@ package wasi:clocks@0.2.1;
 @since(version = 0.2.0)
 interface monotonic-clock {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
 
     /// An instant in time, in nanoseconds. An instant is relative to an
     /// unspecified initial value, and can only be compared to instances from
diff --git a/crates/wasi-http/wit/deps/clocks/timezone.wit b/crates/wasi-http/wit/deps/clocks/timezone.wit
index 212da6682..349fb5703 100644
--- a/crates/wasi-http/wit/deps/clocks/timezone.wit
+++ b/crates/wasi-http/wit/deps/clocks/timezone.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 
 @unstable(feature = clocks-timezone)
 interface timezone {
diff --git a/crates/wasi-http/wit/deps/clocks/wall-clock.wit b/crates/wasi-http/wit/deps/clocks/wall-clock.wit
index 6be069a32..ec05a1f1a 100644
--- a/crates/wasi-http/wit/deps/clocks/wall-clock.wit
+++ b/crates/wasi-http/wit/deps/clocks/wall-clock.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 /// WASI Wall Clock is a clock API intended to let users query the current
 /// time. The name "wall" makes an analogy to a "clock on the wall", which
 /// is not necessarily monotonic as it may be reset.
diff --git a/crates/wasi-http/wit/deps/clocks/world.wit b/crates/wasi-http/wit/deps/clocks/world.wit
index 9251ac645..e36802cc8 100644
--- a/crates/wasi-http/wit/deps/clocks/world.wit
+++ b/crates/wasi-http/wit/deps/clocks/world.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi-http/wit/deps/filesystem/preopens.wit b/crates/wasi-http/wit/deps/filesystem/preopens.wit
index ca2f726af..410bec1dc 100644
--- a/crates/wasi-http/wit/deps/filesystem/preopens.wit
+++ b/crates/wasi-http/wit/deps/filesystem/preopens.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 
 @since(version = 0.2.0)
 interface preopens {
diff --git a/crates/wasi-http/wit/deps/filesystem/types.wit b/crates/wasi-http/wit/deps/filesystem/types.wit
index db3d96867..49e0a30bb 100644
--- a/crates/wasi-http/wit/deps/filesystem/types.wit
+++ b/crates/wasi-http/wit/deps/filesystem/types.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 /// WASI filesystem is a filesystem API primarily intended to let users run WASI
 /// programs that access their files on their existing filesystems, without
 /// significant overhead.
@@ -26,9 +26,9 @@ package wasi:filesystem@0.2.1;
 @since(version = 0.2.0)
 interface types {
     @since(version = 0.2.0)
-    use wasi:io/streams@0.2.1.{input-stream, output-stream, error};
+    use wasi:io/streams@0.2.2.{input-stream, output-stream, error};
     @since(version = 0.2.0)
-    use wasi:clocks/wall-clock@0.2.1.{datetime};
+    use wasi:clocks/wall-clock@0.2.2.{datetime};
 
     /// File size or length of a region within a file.
     @since(version = 0.2.0)
@@ -523,12 +523,6 @@ interface types {
 
         /// Open a file or directory.
         ///
-        /// The returned descriptor is not guaranteed to be the lowest-numbered
-        /// descriptor not currently open/ it is randomized to prevent applications
-        /// from depending on making assumptions about indexes, since this is
-        /// error-prone in multi-threaded contexts. The returned descriptor is
-        /// guaranteed to be less than 2**31.
-        ///
         /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
         /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
         /// `open-at` fails with `error-code::read-only`.
diff --git a/crates/wasi-http/wit/deps/filesystem/world.wit b/crates/wasi-http/wit/deps/filesystem/world.wit
index af0146cbc..8064bd64b 100644
--- a/crates/wasi-http/wit/deps/filesystem/world.wit
+++ b/crates/wasi-http/wit/deps/filesystem/world.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi-http/wit/deps/http/proxy.wit b/crates/wasi-http/wit/deps/http/proxy.wit
index 415d2ee1c..fadb89a3e 100644
--- a/crates/wasi-http/wit/deps/http/proxy.wit
+++ b/crates/wasi-http/wit/deps/http/proxy.wit
@@ -1,4 +1,4 @@
-package wasi:http@0.2.1;
+package wasi:http@0.2.2;
 
 /// The `wasi:http/imports` world imports all the APIs for HTTP proxies.
 /// It is intended to be `include`d in other worlds.
@@ -6,25 +6,25 @@ package wasi:http@0.2.1;
 world imports {
   /// HTTP proxies have access to time and randomness.
   @since(version = 0.2.0)
-  import wasi:clocks/monotonic-clock@0.2.1;
+  import wasi:clocks/monotonic-clock@0.2.2;
   @since(version = 0.2.0)
-  import wasi:clocks/wall-clock@0.2.1;
+  import wasi:clocks/wall-clock@0.2.2;
   @since(version = 0.2.0)
-  import wasi:random/random@0.2.1;
+  import wasi:random/random@0.2.2;
 
   /// Proxies have standard output and error streams which are expected to
   /// terminate in a developer-facing console provided by the host.
   @since(version = 0.2.0)
-  import wasi:cli/stdout@0.2.1;
+  import wasi:cli/stdout@0.2.2;
   @since(version = 0.2.0)
-  import wasi:cli/stderr@0.2.1;
+  import wasi:cli/stderr@0.2.2;
 
   /// TODO: this is a temporary workaround until component tooling is able to
   /// gracefully handle the absence of stdin. Hosts must return an eof stream
   /// for this import, which is what wasi-libc + tooling will do automatically
   /// when this import is properly removed.
   @since(version = 0.2.0)
-  import wasi:cli/stdin@0.2.1;
+  import wasi:cli/stdin@0.2.2;
 
   /// This is the default handler to use when user code simply wants to make an
   /// HTTP request (e.g., via `fetch()`).
diff --git a/crates/wasi-http/wit/deps/http/types.wit b/crates/wasi-http/wit/deps/http/types.wit
index 3c45cd08b..40ee77068 100644
--- a/crates/wasi-http/wit/deps/http/types.wit
+++ b/crates/wasi-http/wit/deps/http/types.wit
@@ -4,13 +4,13 @@
 @since(version = 0.2.0)
 interface types {
   @since(version = 0.2.0)
-  use wasi:clocks/monotonic-clock@0.2.1.{duration};
+  use wasi:clocks/monotonic-clock@0.2.2.{duration};
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{input-stream, output-stream};
+  use wasi:io/streams@0.2.2.{input-stream, output-stream};
   @since(version = 0.2.0)
-  use wasi:io/error@0.2.1.{error as io-error};
+  use wasi:io/error@0.2.2.{error as io-error};
   @since(version = 0.2.0)
-  use wasi:io/poll@0.2.1.{pollable};
+  use wasi:io/poll@0.2.2.{pollable};
 
   /// This type corresponds to HTTP standard Methods.
   @since(version = 0.2.0)
@@ -124,12 +124,12 @@ interface types {
   /// setting or appending to a `fields` resource.
   @since(version = 0.2.0)
   variant header-error {
-    /// This error indicates that a `field-key` or `field-value` was
+    /// This error indicates that a `field-name` or `field-value` was
     /// syntactically invalid when used with an operation that sets headers in a
     /// `fields`.
     invalid-syntax,
 
-    /// This error indicates that a forbidden `field-key` was used when trying
+    /// This error indicates that a forbidden `field-name` was used when trying
     /// to set a header in a `fields`.
     forbidden,
 
@@ -138,8 +138,23 @@ interface types {
     immutable,
   }
 
+  /// Field names are always strings.
+  ///
+  /// Field names should always be treated as case insensitive by the `fields`
+  /// resource for the purposes of equality checking.
+  @since(version = 0.2.1)
+  type field-name = field-key;
+
   /// Field keys are always strings.
+  ///
+  /// Field keys should always be treated as case insensitive by the `fields`
+  /// resource for the purposes of equality checking.
+  /// 
+  /// # Deprecation
+  /// 
+  /// This type has been deprecated in favor of the `field-name` type.
   @since(version = 0.2.0)
+  @deprecated(version = 0.2.2)
   type field-key = string;
 
   /// Field values should always be ASCII strings. However, in
@@ -171,70 +186,73 @@ interface types {
     ///
     /// The resulting `fields` is mutable.
     ///
-    /// The list represents each key-value pair in the Fields. Keys
+    /// The list represents each name-value pair in the Fields. Names
     /// which have multiple values are represented by multiple entries in this
-    /// list with the same key.
+    /// list with the same name.
     ///
-    /// The tuple is a pair of the field key, represented as a string, and
+    /// The tuple is a pair of the field name, represented as a string, and
     /// Value, represented as a list of bytes.
     ///
-    /// An error result will be returned if any `field-key` or `field-value` is
+    /// An error result will be returned if any `field-name` or `field-value` is
     /// syntactically invalid, or if a field is forbidden.
     @since(version = 0.2.0)
     from-list: static func(
-      entries: list<tuple<field-key,field-value>>
+      entries: list<tuple<field-name,field-value>>
     ) -> result<fields, header-error>;
 
-    /// Get all of the values corresponding to a key. If the key is not present
+    /// Get all of the values corresponding to a name. If the name is not present
     /// in this `fields` or is syntactically invalid, an empty list is returned.
-    /// However, if the key is present but empty, this is represented by a list
+    /// However, if the name is present but empty, this is represented by a list
     /// with one or more empty field-values present.
     @since(version = 0.2.0)
-    get: func(name: field-key) -> list<field-value>;
+    get: func(name: field-name) -> list<field-value>;
 
-    /// Returns `true` when the key is present in this `fields`. If the key is
+    /// Returns `true` when the name is present in this `fields`. If the name is
     /// syntactically invalid, `false` is returned.
     @since(version = 0.2.0)
-    has: func(name: field-key) -> bool;
+    has: func(name: field-name) -> bool;
 
-    /// Set all of the values for a key. Clears any existing values for that
-    /// key, if they have been set.
+    /// Set all of the values for a name. Clears any existing values for that
+    /// name, if they have been set.
     ///
     /// Fails with `header-error.immutable` if the `fields` are immutable.
     ///
-    /// Fails with `header-error.invalid-syntax` if the `field-key` or any of
+    /// Fails with `header-error.invalid-syntax` if the `field-name` or any of
     /// the `field-value`s are syntactically invalid.
     @since(version = 0.2.0)
-    set: func(name: field-key, value: list<field-value>) -> result<_, header-error>;
+    set: func(name: field-name, value: list<field-value>) -> result<_, header-error>;
 
-    /// Delete all values for a key. Does nothing if no values for the key
+    /// Delete all values for a name. Does nothing if no values for the name
     /// exist.
     ///
     /// Fails with `header-error.immutable` if the `fields` are immutable.
     ///
-    /// Fails with `header-error.invalid-syntax` if the `field-key` is
+    /// Fails with `header-error.invalid-syntax` if the `field-name` is
     /// syntactically invalid.
     @since(version = 0.2.0)
-    delete: func(name: field-key) -> result<_, header-error>;
+    delete: func(name: field-name) -> result<_, header-error>;
 
-    /// Append a value for a key. Does not change or delete any existing
-    /// values for that key.
+    /// Append a value for a name. Does not change or delete any existing
+    /// values for that name.
     ///
     /// Fails with `header-error.immutable` if the `fields` are immutable.
     ///
-    /// Fails with `header-error.invalid-syntax` if the `field-key` or
+    /// Fails with `header-error.invalid-syntax` if the `field-name` or
     /// `field-value` are syntactically invalid.
     @since(version = 0.2.0)
-    append: func(name: field-key, value: field-value) -> result<_, header-error>;
+    append: func(name: field-name, value: field-value) -> result<_, header-error>;
 
-    /// Retrieve the full set of keys and values in the Fields. Like the
-    /// constructor, the list represents each key-value pair.
+    /// Retrieve the full set of names and values in the Fields. Like the
+    /// constructor, the list represents each name-value pair.
     ///
-    /// The outer list represents each key-value pair in the Fields. Keys
+    /// The outer list represents each name-value pair in the Fields. Names
     /// which have multiple values are represented by multiple entries in this
-    /// list with the same key.
+    /// list with the same name.
+    ///
+    /// The names and values are always returned in the original casing and in
+    /// the order in which they will be serialized for transport.
     @since(version = 0.2.0)
-    entries: func() -> list<tuple<field-key,field-value>>;
+    entries: func() -> list<tuple<field-name,field-value>>;
 
     /// Make a deep copy of the Fields. Equivalent in behavior to calling the
     /// `fields` constructor on the return value of `entries`. The resulting
diff --git a/crates/wasi-http/wit/deps/io/error.wit b/crates/wasi-http/wit/deps/io/error.wit
index 4ea29c469..717135f8c 100644
--- a/crates/wasi-http/wit/deps/io/error.wit
+++ b/crates/wasi-http/wit/deps/io/error.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 @since(version = 0.2.0)
 interface error {
diff --git a/crates/wasi-http/wit/deps/io/poll.wit b/crates/wasi-http/wit/deps/io/poll.wit
index b25ac729f..49c1c5ede 100644
--- a/crates/wasi-http/wit/deps/io/poll.wit
+++ b/crates/wasi-http/wit/deps/io/poll.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 /// A poll API intended to let users wait for I/O events on multiple handles
 /// at once.
diff --git a/crates/wasi-http/wit/deps/io/streams.wit b/crates/wasi-http/wit/deps/io/streams.wit
index b697e24d6..330f7095c 100644
--- a/crates/wasi-http/wit/deps/io/streams.wit
+++ b/crates/wasi-http/wit/deps/io/streams.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 /// WASI I/O is an I/O abstraction API which is currently focused on providing
 /// stream types.
@@ -18,6 +18,9 @@ interface streams {
         /// The last operation (a write or flush) failed before completion.
         ///
         /// More information is available in the `error` payload.
+        ///
+        /// After this, the stream will be closed. All future operations return
+        /// `stream-error::closed`.
         last-operation-failed(error),
         /// The stream is closed: no more input will be accepted by the
         /// stream. A closed output-stream will return this error on all
@@ -205,6 +208,7 @@ interface streams {
         /// The created `pollable` is a child resource of the `output-stream`.
         /// Implementations may trap if the `output-stream` is dropped before
         /// all derived `pollable`s created with this function are dropped.
+        @since(version = 0.2.0)
         subscribe: func() -> pollable;
 
         /// Write zeroes to a stream.
diff --git a/crates/wasi-http/wit/deps/io/world.wit b/crates/wasi-http/wit/deps/io/world.wit
index 6405a4e48..f7001ccff 100644
--- a/crates/wasi-http/wit/deps/io/world.wit
+++ b/crates/wasi-http/wit/deps/io/world.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi-http/wit/deps/random/insecure-seed.wit b/crates/wasi-http/wit/deps/random/insecure-seed.wit
index 7e708dc52..cdea716cf 100644
--- a/crates/wasi-http/wit/deps/random/insecure-seed.wit
+++ b/crates/wasi-http/wit/deps/random/insecure-seed.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// The insecure-seed interface for seeding hash-map DoS resistance.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi-http/wit/deps/random/insecure.wit b/crates/wasi-http/wit/deps/random/insecure.wit
index 3cdb53dfb..b71e85879 100644
--- a/crates/wasi-http/wit/deps/random/insecure.wit
+++ b/crates/wasi-http/wit/deps/random/insecure.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// The insecure interface for insecure pseudo-random numbers.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi-http/wit/deps/random/random.wit b/crates/wasi-http/wit/deps/random/random.wit
index 2b5035d1c..0c57e8c80 100644
--- a/crates/wasi-http/wit/deps/random/random.wit
+++ b/crates/wasi-http/wit/deps/random/random.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// WASI Random is a random data API.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi-http/wit/deps/random/world.wit b/crates/wasi-http/wit/deps/random/world.wit
index c615e96dc..16d68acfa 100644
--- a/crates/wasi-http/wit/deps/random/world.wit
+++ b/crates/wasi-http/wit/deps/random/world.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
index dc56f3000..d3ab88aed 100644
--- a/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
+++ b/crates/wasi-http/wit/deps/sockets/ip-name-lookup.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface ip-name-lookup {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-address};
 
@@ -48,7 +48,7 @@ interface ip-name-lookup {
 
         /// Create a `pollable` which will resolve once the stream is ready for I/O.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi-http/wit/deps/sockets/network.wit b/crates/wasi-http/wit/deps/sockets/network.wit
index 8c13b348e..7f2d86a4c 100644
--- a/crates/wasi-http/wit/deps/sockets/network.wit
+++ b/crates/wasi-http/wit/deps/sockets/network.wit
@@ -1,5 +1,8 @@
 @since(version = 0.2.0)
 interface network {
+    @unstable(feature = network-error-code)
+    use wasi:io/error@0.2.2.{error};
+
     /// An opaque resource that represents access to (a subset of) the network.
     /// This enables context-based security for networking.
     /// There is no need for this to map 1:1 to a physical network interface.
@@ -105,6 +108,19 @@ interface network {
         permanent-resolver-failure,
     }
 
+    /// Attempts to extract a network-related `error-code` from the stream
+    /// `error` provided.
+    ///
+    /// Stream operations which return `stream-error::last-operation-failed`
+    /// have a payload with more information about the operation that failed.
+    /// This payload can be passed through to this function to see if there's
+    /// network-related information about the error to return.
+    ///
+    /// Note that this function is fallible because not all stream-related
+    /// errors are network-related errors.
+    @unstable(feature = network-error-code)
+    network-error-code: func(err: borrow<error>) -> option<error-code>;
+
     @since(version = 0.2.0)
     enum ip-address-family {
         /// Similar to `AF_INET` in POSIX.
diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit
index bae5a29ec..728822dfa 100644
--- a/crates/wasi-http/wit/deps/sockets/tcp.wit
+++ b/crates/wasi-http/wit/deps/sockets/tcp.wit
@@ -1,11 +1,11 @@
 @since(version = 0.2.0)
 interface tcp {
     @since(version = 0.2.0)
-    use wasi:io/streams@0.2.1.{input-stream, output-stream};
+    use wasi:io/streams@0.2.2.{input-stream, output-stream};
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
-    use wasi:clocks/monotonic-clock@0.2.1.{duration};
+    use wasi:clocks/monotonic-clock@0.2.2.{duration};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-socket-address, ip-address-family};
 
@@ -353,7 +353,7 @@ interface tcp {
         /// See <https://github.com/WebAssembly/wasi-sockets/blob/main/TcpSocketOperationalSemantics.md#pollable-readiness>
         /// for more information.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi-http/wit/deps/sockets/udp.wit b/crates/wasi-http/wit/deps/sockets/udp.wit
index b289e4943..d8acb2d29 100644
--- a/crates/wasi-http/wit/deps/sockets/udp.wit
+++ b/crates/wasi-http/wit/deps/sockets/udp.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface udp {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-socket-address, ip-address-family};
 
@@ -184,7 +184,7 @@ interface udp {
 
         /// Create a `pollable` which will resolve once the socket is ready for I/O.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
@@ -220,7 +220,7 @@ interface udp {
 
         /// Create a `pollable` which will resolve once the stream is ready to receive again.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
@@ -280,7 +280,7 @@ interface udp {
         
         /// Create a `pollable` which will resolve once the stream is ready to send again.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi-http/wit/deps/sockets/world.wit b/crates/wasi-http/wit/deps/sockets/world.wit
index a1d42670e..6e349c756 100644
--- a/crates/wasi-http/wit/deps/sockets/world.wit
+++ b/crates/wasi-http/wit/deps/sockets/world.wit
@@ -1,4 +1,4 @@
-package wasi:sockets@0.2.1;
+package wasi:sockets@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi-http/wit/world.wit b/crates/wasi-http/wit/world.wit
index 0875bb306..75aa81ff0 100644
--- a/crates/wasi-http/wit/world.wit
+++ b/crates/wasi-http/wit/world.wit
@@ -2,5 +2,5 @@
 package wasmtime:wasi-http;
 
 world bindings {
-  include wasi:http/proxy@0.2.1;
+  include wasi:http/proxy@0.2.2;
 }
diff --git a/crates/wasi-preview1-component-adapter/src/descriptors.rs b/crates/wasi-preview1-component-adapter/src/descriptors.rs
index d534bb6b5..80a1ede00 100644
--- a/crates/wasi-preview1-component-adapter/src/descriptors.rs
+++ b/crates/wasi-preview1-component-adapter/src/descriptors.rs
@@ -149,7 +149,7 @@ pub struct Descriptors {
 }
 
 #[cfg(not(feature = "proxy"))]
-#[link(wasm_import_module = "wasi:filesystem/preopens@0.2.1")]
+#[link(wasm_import_module = "wasi:filesystem/preopens@0.2.2")]
 extern "C" {
     #[link_name = "get-directories"]
     fn wasi_filesystem_get_directories(rval: *mut PreopenList);
diff --git a/crates/wasi-preview1-component-adapter/src/lib.rs b/crates/wasi-preview1-component-adapter/src/lib.rs
index 1806df36f..f3d9b9710 100644
--- a/crates/wasi-preview1-component-adapter/src/lib.rs
+++ b/crates/wasi-preview1-component-adapter/src/lib.rs
@@ -91,12 +91,12 @@ pub mod bindings {
             package wasmtime:adapter;
 
             world adapter {
-                import wasi:clocks/wall-clock@0.2.1;
-                import wasi:clocks/monotonic-clock@0.2.1;
-                import wasi:random/random@0.2.1;
-                import wasi:cli/stdout@0.2.1;
-                import wasi:cli/stderr@0.2.1;
-                import wasi:cli/stdin@0.2.1;
+                import wasi:clocks/wall-clock@0.2.2;
+                import wasi:clocks/monotonic-clock@0.2.2;
+                import wasi:random/random@0.2.2;
+                import wasi:cli/stdout@0.2.2;
+                import wasi:cli/stderr@0.2.2;
+                import wasi:cli/stdin@0.2.2;
             }
         "#,
         world: "wasmtime:adapter/adapter",
@@ -115,7 +115,7 @@ pub mod bindings {
     }
 }
 
-#[export_name = "wasi:cli/run@0.2.1#run"]
+#[export_name = "wasi:cli/run@0.2.2#run"]
 #[cfg(feature = "command")]
 pub unsafe extern "C" fn run() -> u32 {
     #[link(wasm_import_module = "__main_module__")]
@@ -457,7 +457,7 @@ impl BumpAlloc {
 }
 
 #[cfg(not(feature = "proxy"))]
-#[link(wasm_import_module = "wasi:cli/environment@0.2.1")]
+#[link(wasm_import_module = "wasi:cli/environment@0.2.2")]
 extern "C" {
     #[link_name = "get-arguments"]
     fn wasi_cli_get_arguments(rval: *mut WasmStrList);
@@ -2156,7 +2156,7 @@ pub unsafe extern "C" fn poll_oneoff(
             });
         }
 
-        #[link(wasm_import_module = "wasi:io/poll@0.2.1")]
+        #[link(wasm_import_module = "wasi:io/poll@0.2.2")]
         #[allow(improper_ctypes)] // FIXME(bytecodealliance/wit-bindgen#684)
         extern "C" {
             #[link_name = "poll"]
diff --git a/crates/wasi/src/bindings.rs b/crates/wasi/src/bindings.rs
index c236bc3f0..72c2c3ff9 100644
--- a/crates/wasi/src/bindings.rs
+++ b/crates/wasi/src/bindings.rs
@@ -26,7 +26,7 @@
 //!         // An example of extending the `wasi:cli/command` world with a
 //!         // custom host interface.
 //!         world my-world {
-//!             include wasi:cli/command@0.2.1;
+//!             include wasi:cli/command@0.2.2;
 //!
 //!             import custom-host;
 //!         }
@@ -96,7 +96,7 @@
 ///         // An example of extending the `wasi:cli/command` world with a
 ///         // custom host interface.
 ///         world my-world {
-///             include wasi:cli/command@0.2.1;
+///             include wasi:cli/command@0.2.2;
 ///
 ///             import custom-host;
 ///         }
diff --git a/crates/wasi/src/host/network.rs b/crates/wasi/src/host/network.rs
index 6772db788..3f7f4b827 100644
--- a/crates/wasi/src/host/network.rs
+++ b/crates/wasi/src/host/network.rs
@@ -4,6 +4,7 @@ use crate::bindings::sockets::network::{
 };
 use crate::network::{from_ipv4_addr, from_ipv6_addr, to_ipv4_addr, to_ipv6_addr};
 use crate::{SocketError, WasiImpl, WasiView};
+use anyhow::Error;
 use rustix::io::Errno;
 use std::io;
 use wasmtime::component::Resource;
@@ -15,6 +16,12 @@ where
     fn convert_error_code(&mut self, error: SocketError) -> anyhow::Result<ErrorCode> {
         error.downcast()
     }
+
+    fn network_error_code(&mut self, err: Resource<Error>) -> anyhow::Result<Option<ErrorCode>> {
+        let err = self.table().get(&err)?;
+        let _ = err; // TODO: should fill in this implementation
+        Ok(None)
+    }
 }
 
 impl<T> crate::bindings::sockets::network::HostNetwork for WasiImpl<T>
diff --git a/crates/wasi/src/lib.rs b/crates/wasi/src/lib.rs
index 8fb740cda..ddb389964 100644
--- a/crates/wasi/src/lib.rs
+++ b/crates/wasi/src/lib.rs
@@ -323,7 +323,7 @@ pub fn add_to_linker_with_options_async<T: WasiView>(
     crate::bindings::sockets::udp::add_to_linker_get_host(l, closure)?;
     crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
     crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
-    crate::bindings::sockets::network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::network::add_to_linker_get_host(l, &options.into(), closure)?;
     crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
     Ok(())
 }
@@ -422,7 +422,7 @@ pub fn add_to_linker_with_options_sync<T: WasiView>(
     crate::bindings::sync::sockets::udp::add_to_linker_get_host(l, closure)?;
     crate::bindings::sockets::udp_create_socket::add_to_linker_get_host(l, closure)?;
     crate::bindings::sockets::instance_network::add_to_linker_get_host(l, closure)?;
-    crate::bindings::sockets::network::add_to_linker_get_host(l, closure)?;
+    crate::bindings::sockets::network::add_to_linker_get_host(l, &options.into(), closure)?;
     crate::bindings::sockets::ip_name_lookup::add_to_linker_get_host(l, closure)?;
     Ok(())
 }
diff --git a/crates/wasi/wit/deps/cli/command.wit b/crates/wasi/wit/deps/cli/command.wit
index dc064a3cd..cc7a352c2 100644
--- a/crates/wasi/wit/deps/cli/command.wit
+++ b/crates/wasi/wit/deps/cli/command.wit
@@ -1,4 +1,4 @@
-package wasi:cli@0.2.1;
+package wasi:cli@0.2.2;
 
 @since(version = 0.2.0)
 world command {
diff --git a/crates/wasi/wit/deps/cli/imports.wit b/crates/wasi/wit/deps/cli/imports.wit
index b8339d3b2..ebd7ba173 100644
--- a/crates/wasi/wit/deps/cli/imports.wit
+++ b/crates/wasi/wit/deps/cli/imports.wit
@@ -1,17 +1,17 @@
-package wasi:cli@0.2.1;
+package wasi:cli@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
   @since(version = 0.2.0)
-  include wasi:clocks/imports@0.2.1;
+  include wasi:clocks/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:filesystem/imports@0.2.1;
+  include wasi:filesystem/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:sockets/imports@0.2.1;
+  include wasi:sockets/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:random/imports@0.2.1;
+  include wasi:random/imports@0.2.2;
   @since(version = 0.2.0)
-  include wasi:io/imports@0.2.1;
+  include wasi:io/imports@0.2.2;
 
   @since(version = 0.2.0)
   import environment;
diff --git a/crates/wasi/wit/deps/cli/stdio.wit b/crates/wasi/wit/deps/cli/stdio.wit
index d1d26eb61..860313eea 100644
--- a/crates/wasi/wit/deps/cli/stdio.wit
+++ b/crates/wasi/wit/deps/cli/stdio.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface stdin {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{input-stream};
+  use wasi:io/streams@0.2.2.{input-stream};
 
   @since(version = 0.2.0)
   get-stdin: func() -> input-stream;
@@ -10,7 +10,7 @@ interface stdin {
 @since(version = 0.2.0)
 interface stdout {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{output-stream};
+  use wasi:io/streams@0.2.2.{output-stream};
 
   @since(version = 0.2.0)
   get-stdout: func() -> output-stream;
@@ -19,7 +19,7 @@ interface stdout {
 @since(version = 0.2.0)
 interface stderr {
   @since(version = 0.2.0)
-  use wasi:io/streams@0.2.1.{output-stream};
+  use wasi:io/streams@0.2.2.{output-stream};
 
   @since(version = 0.2.0)
   get-stderr: func() -> output-stream;
diff --git a/crates/wasi/wit/deps/clocks/monotonic-clock.wit b/crates/wasi/wit/deps/clocks/monotonic-clock.wit
index 3c24840c9..233cace4c 100644
--- a/crates/wasi/wit/deps/clocks/monotonic-clock.wit
+++ b/crates/wasi/wit/deps/clocks/monotonic-clock.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 /// WASI Monotonic Clock is a clock API intended to let users measure elapsed
 /// time.
 ///
@@ -10,7 +10,7 @@ package wasi:clocks@0.2.1;
 @since(version = 0.2.0)
 interface monotonic-clock {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
 
     /// An instant in time, in nanoseconds. An instant is relative to an
     /// unspecified initial value, and can only be compared to instances from
diff --git a/crates/wasi/wit/deps/clocks/timezone.wit b/crates/wasi/wit/deps/clocks/timezone.wit
index 212da6682..349fb5703 100644
--- a/crates/wasi/wit/deps/clocks/timezone.wit
+++ b/crates/wasi/wit/deps/clocks/timezone.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 
 @unstable(feature = clocks-timezone)
 interface timezone {
diff --git a/crates/wasi/wit/deps/clocks/wall-clock.wit b/crates/wasi/wit/deps/clocks/wall-clock.wit
index 6be069a32..ec05a1f1a 100644
--- a/crates/wasi/wit/deps/clocks/wall-clock.wit
+++ b/crates/wasi/wit/deps/clocks/wall-clock.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 /// WASI Wall Clock is a clock API intended to let users query the current
 /// time. The name "wall" makes an analogy to a "clock on the wall", which
 /// is not necessarily monotonic as it may be reset.
diff --git a/crates/wasi/wit/deps/clocks/world.wit b/crates/wasi/wit/deps/clocks/world.wit
index 9251ac645..e36802cc8 100644
--- a/crates/wasi/wit/deps/clocks/world.wit
+++ b/crates/wasi/wit/deps/clocks/world.wit
@@ -1,4 +1,4 @@
-package wasi:clocks@0.2.1;
+package wasi:clocks@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi/wit/deps/filesystem/preopens.wit b/crates/wasi/wit/deps/filesystem/preopens.wit
index ca2f726af..410bec1dc 100644
--- a/crates/wasi/wit/deps/filesystem/preopens.wit
+++ b/crates/wasi/wit/deps/filesystem/preopens.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 
 @since(version = 0.2.0)
 interface preopens {
diff --git a/crates/wasi/wit/deps/filesystem/types.wit b/crates/wasi/wit/deps/filesystem/types.wit
index db3d96867..49e0a30bb 100644
--- a/crates/wasi/wit/deps/filesystem/types.wit
+++ b/crates/wasi/wit/deps/filesystem/types.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 /// WASI filesystem is a filesystem API primarily intended to let users run WASI
 /// programs that access their files on their existing filesystems, without
 /// significant overhead.
@@ -26,9 +26,9 @@ package wasi:filesystem@0.2.1;
 @since(version = 0.2.0)
 interface types {
     @since(version = 0.2.0)
-    use wasi:io/streams@0.2.1.{input-stream, output-stream, error};
+    use wasi:io/streams@0.2.2.{input-stream, output-stream, error};
     @since(version = 0.2.0)
-    use wasi:clocks/wall-clock@0.2.1.{datetime};
+    use wasi:clocks/wall-clock@0.2.2.{datetime};
 
     /// File size or length of a region within a file.
     @since(version = 0.2.0)
@@ -523,12 +523,6 @@ interface types {
 
         /// Open a file or directory.
         ///
-        /// The returned descriptor is not guaranteed to be the lowest-numbered
-        /// descriptor not currently open/ it is randomized to prevent applications
-        /// from depending on making assumptions about indexes, since this is
-        /// error-prone in multi-threaded contexts. The returned descriptor is
-        /// guaranteed to be less than 2**31.
-        ///
         /// If `flags` contains `descriptor-flags::mutate-directory`, and the base
         /// descriptor doesn't have `descriptor-flags::mutate-directory` set,
         /// `open-at` fails with `error-code::read-only`.
diff --git a/crates/wasi/wit/deps/filesystem/world.wit b/crates/wasi/wit/deps/filesystem/world.wit
index af0146cbc..8064bd64b 100644
--- a/crates/wasi/wit/deps/filesystem/world.wit
+++ b/crates/wasi/wit/deps/filesystem/world.wit
@@ -1,4 +1,4 @@
-package wasi:filesystem@0.2.1;
+package wasi:filesystem@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi/wit/deps/io/error.wit b/crates/wasi/wit/deps/io/error.wit
index 4ea29c469..717135f8c 100644
--- a/crates/wasi/wit/deps/io/error.wit
+++ b/crates/wasi/wit/deps/io/error.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 @since(version = 0.2.0)
 interface error {
diff --git a/crates/wasi/wit/deps/io/poll.wit b/crates/wasi/wit/deps/io/poll.wit
index b25ac729f..49c1c5ede 100644
--- a/crates/wasi/wit/deps/io/poll.wit
+++ b/crates/wasi/wit/deps/io/poll.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 /// A poll API intended to let users wait for I/O events on multiple handles
 /// at once.
diff --git a/crates/wasi/wit/deps/io/streams.wit b/crates/wasi/wit/deps/io/streams.wit
index b697e24d6..330f7095c 100644
--- a/crates/wasi/wit/deps/io/streams.wit
+++ b/crates/wasi/wit/deps/io/streams.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 /// WASI I/O is an I/O abstraction API which is currently focused on providing
 /// stream types.
@@ -18,6 +18,9 @@ interface streams {
         /// The last operation (a write or flush) failed before completion.
         ///
         /// More information is available in the `error` payload.
+        ///
+        /// After this, the stream will be closed. All future operations return
+        /// `stream-error::closed`.
         last-operation-failed(error),
         /// The stream is closed: no more input will be accepted by the
         /// stream. A closed output-stream will return this error on all
@@ -205,6 +208,7 @@ interface streams {
         /// The created `pollable` is a child resource of the `output-stream`.
         /// Implementations may trap if the `output-stream` is dropped before
         /// all derived `pollable`s created with this function are dropped.
+        @since(version = 0.2.0)
         subscribe: func() -> pollable;
 
         /// Write zeroes to a stream.
diff --git a/crates/wasi/wit/deps/io/world.wit b/crates/wasi/wit/deps/io/world.wit
index 6405a4e48..f7001ccff 100644
--- a/crates/wasi/wit/deps/io/world.wit
+++ b/crates/wasi/wit/deps/io/world.wit
@@ -1,4 +1,4 @@
-package wasi:io@0.2.1;
+package wasi:io@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi/wit/deps/random/insecure-seed.wit b/crates/wasi/wit/deps/random/insecure-seed.wit
index 7e708dc52..cdea716cf 100644
--- a/crates/wasi/wit/deps/random/insecure-seed.wit
+++ b/crates/wasi/wit/deps/random/insecure-seed.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// The insecure-seed interface for seeding hash-map DoS resistance.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi/wit/deps/random/insecure.wit b/crates/wasi/wit/deps/random/insecure.wit
index 3cdb53dfb..b71e85879 100644
--- a/crates/wasi/wit/deps/random/insecure.wit
+++ b/crates/wasi/wit/deps/random/insecure.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// The insecure interface for insecure pseudo-random numbers.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi/wit/deps/random/random.wit b/crates/wasi/wit/deps/random/random.wit
index 2b5035d1c..0c57e8c80 100644
--- a/crates/wasi/wit/deps/random/random.wit
+++ b/crates/wasi/wit/deps/random/random.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 /// WASI Random is a random data API.
 ///
 /// It is intended to be portable at least between Unix-family platforms and
diff --git a/crates/wasi/wit/deps/random/world.wit b/crates/wasi/wit/deps/random/world.wit
index c615e96dc..16d68acfa 100644
--- a/crates/wasi/wit/deps/random/world.wit
+++ b/crates/wasi/wit/deps/random/world.wit
@@ -1,4 +1,4 @@
-package wasi:random@0.2.1;
+package wasi:random@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
index dc56f3000..d3ab88aed 100644
--- a/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
+++ b/crates/wasi/wit/deps/sockets/ip-name-lookup.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface ip-name-lookup {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-address};
 
@@ -48,7 +48,7 @@ interface ip-name-lookup {
 
         /// Create a `pollable` which will resolve once the stream is ready for I/O.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi/wit/deps/sockets/network.wit b/crates/wasi/wit/deps/sockets/network.wit
index 8c13b348e..7f2d86a4c 100644
--- a/crates/wasi/wit/deps/sockets/network.wit
+++ b/crates/wasi/wit/deps/sockets/network.wit
@@ -1,5 +1,8 @@
 @since(version = 0.2.0)
 interface network {
+    @unstable(feature = network-error-code)
+    use wasi:io/error@0.2.2.{error};
+
     /// An opaque resource that represents access to (a subset of) the network.
     /// This enables context-based security for networking.
     /// There is no need for this to map 1:1 to a physical network interface.
@@ -105,6 +108,19 @@ interface network {
         permanent-resolver-failure,
     }
 
+    /// Attempts to extract a network-related `error-code` from the stream
+    /// `error` provided.
+    ///
+    /// Stream operations which return `stream-error::last-operation-failed`
+    /// have a payload with more information about the operation that failed.
+    /// This payload can be passed through to this function to see if there's
+    /// network-related information about the error to return.
+    ///
+    /// Note that this function is fallible because not all stream-related
+    /// errors are network-related errors.
+    @unstable(feature = network-error-code)
+    network-error-code: func(err: borrow<error>) -> option<error-code>;
+
     @since(version = 0.2.0)
     enum ip-address-family {
         /// Similar to `AF_INET` in POSIX.
diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit
index bae5a29ec..728822dfa 100644
--- a/crates/wasi/wit/deps/sockets/tcp.wit
+++ b/crates/wasi/wit/deps/sockets/tcp.wit
@@ -1,11 +1,11 @@
 @since(version = 0.2.0)
 interface tcp {
     @since(version = 0.2.0)
-    use wasi:io/streams@0.2.1.{input-stream, output-stream};
+    use wasi:io/streams@0.2.2.{input-stream, output-stream};
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
-    use wasi:clocks/monotonic-clock@0.2.1.{duration};
+    use wasi:clocks/monotonic-clock@0.2.2.{duration};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-socket-address, ip-address-family};
 
@@ -353,7 +353,7 @@ interface tcp {
         /// See <https://github.com/WebAssembly/wasi-sockets/blob/main/TcpSocketOperationalSemantics.md#pollable-readiness>
         /// for more information.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi/wit/deps/sockets/udp.wit b/crates/wasi/wit/deps/sockets/udp.wit
index b289e4943..d8acb2d29 100644
--- a/crates/wasi/wit/deps/sockets/udp.wit
+++ b/crates/wasi/wit/deps/sockets/udp.wit
@@ -1,7 +1,7 @@
 @since(version = 0.2.0)
 interface udp {
     @since(version = 0.2.0)
-    use wasi:io/poll@0.2.1.{pollable};
+    use wasi:io/poll@0.2.2.{pollable};
     @since(version = 0.2.0)
     use network.{network, error-code, ip-socket-address, ip-address-family};
 
@@ -184,7 +184,7 @@ interface udp {
 
         /// Create a `pollable` which will resolve once the socket is ready for I/O.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
@@ -220,7 +220,7 @@ interface udp {
 
         /// Create a `pollable` which will resolve once the stream is ready to receive again.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
@@ -280,7 +280,7 @@ interface udp {
         
         /// Create a `pollable` which will resolve once the stream is ready to send again.
         ///
-        /// Note: this function is here for WASI Preview2 only.
+        /// Note: this function is here for WASI 0.2 only.
         /// It's planned to be removed when `future` is natively supported in Preview3.
         @since(version = 0.2.0)
         subscribe: func() -> pollable;
diff --git a/crates/wasi/wit/deps/sockets/world.wit b/crates/wasi/wit/deps/sockets/world.wit
index a1d42670e..6e349c756 100644
--- a/crates/wasi/wit/deps/sockets/world.wit
+++ b/crates/wasi/wit/deps/sockets/world.wit
@@ -1,4 +1,4 @@
-package wasi:sockets@0.2.1;
+package wasi:sockets@0.2.2;
 
 @since(version = 0.2.0)
 world imports {
diff --git a/crates/wasi/wit/world.wit b/crates/wasi/wit/world.wit
index e540b1f6d..0b3c62819 100644
--- a/crates/wasi/wit/world.wit
+++ b/crates/wasi/wit/world.wit
@@ -2,5 +2,5 @@
 package wasmtime:wasi;
 
 world bindings {
-  include wasi:cli/imports@0.2.1;
+  include wasi:cli/imports@0.2.2;
 }
diff --git a/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs
index 171cf010a..f3a9def96 100644
--- a/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs
+++ b/crates/wasmtime/src/runtime/vm/sys/custom/mmap.rs
@@ -15,7 +15,7 @@ pub struct Mmap {
 impl Mmap {
     pub fn new_empty() -> Mmap {
         Mmap {
-            memory: SendSyncPtr::from(&mut [][..]),
+            memory: crate::vm::sys::empty_mmap(),
         }
     }
 
diff --git a/crates/wasmtime/src/runtime/vm/sys/miri/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/miri/mmap.rs
index de2a6aa3f..625b56bd4 100644
--- a/crates/wasmtime/src/runtime/vm/sys/miri/mmap.rs
+++ b/crates/wasmtime/src/runtime/vm/sys/miri/mmap.rs
@@ -21,7 +21,7 @@ pub struct Mmap {
 impl Mmap {
     pub fn new_empty() -> Mmap {
         Mmap {
-            memory: SendSyncPtr::from(&mut [][..]),
+            memory: crate::vm::sys::empty_mmap(),
         }
     }
 
diff --git a/crates/wasmtime/src/runtime/vm/sys/mod.rs b/crates/wasmtime/src/runtime/vm/sys/mod.rs
index e25ebe305..f67bc312c 100644
--- a/crates/wasmtime/src/runtime/vm/sys/mod.rs
+++ b/crates/wasmtime/src/runtime/vm/sys/mod.rs
@@ -8,6 +8,9 @@
 
 #![allow(clippy::cast_sign_loss)] // platforms too fiddly to worry about this
 
+use crate::runtime::vm::SendSyncPtr;
+use core::ptr::{self, NonNull};
+
 /// What happens to a mapping after it is decommitted?
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum DecommitBehavior {
@@ -19,6 +22,30 @@ pub enum DecommitBehavior {
     RestoreOriginalMapping,
 }
 
+fn empty_mmap() -> SendSyncPtr<[u8]> {
+    // Callers of this API assume that `.as_ptr()` below returns something
+    // page-aligned and non-null. This is because the pointer returned from
+    // that location is casted to other types which reside at a higher
+    // alignment than a byte for example. Despite the length being zero we
+    // still need to ensure that the pointer is suitably aligned.
+    //
+    // To handle that do a bit of trickery here to get the compiler to
+    // generate an empty array to a high-alignment type (here 4k which is
+    // the min page size we work with today). Then use this empty array as
+    // the source pointer for an empty byte slice. It's a bit wonky but this
+    // makes it such that the returned length is always zero (so this is
+    // safe) but the pointer is always 4096 or suitably aligned.
+    #[repr(C, align(4096))]
+    struct PageAligned;
+    let empty_page_alloc: &mut [PageAligned] = &mut [];
+    let empty = NonNull::new(ptr::slice_from_raw_parts_mut(
+        empty_page_alloc.as_mut_ptr().cast(),
+        0,
+    ))
+    .unwrap();
+    SendSyncPtr::from(empty)
+}
+
 cfg_if::cfg_if! {
     if #[cfg(miri)] {
         mod miri;
diff --git a/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs
index 4c29a9ae1..c176de499 100644
--- a/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs
+++ b/crates/wasmtime/src/runtime/vm/sys/unix/mmap.rs
@@ -14,7 +14,7 @@ pub struct Mmap {
 impl Mmap {
     pub fn new_empty() -> Mmap {
         Mmap {
-            memory: SendSyncPtr::from(&mut [][..]),
+            memory: crate::vm::sys::empty_mmap(),
         }
     }
 
diff --git a/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs b/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs
index 13850e26d..f4a986d91 100644
--- a/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs
+++ b/crates/wasmtime/src/runtime/vm/sys/windows/mmap.rs
@@ -19,7 +19,7 @@ pub struct Mmap {
 impl Mmap {
     pub fn new_empty() -> Mmap {
         Mmap {
-            memory: SendSyncPtr::from(&mut [][..]),
+            memory: crate::vm::sys::empty_mmap(),
             is_file: false,
         }
     }
diff --git a/winch/codegen/src/isa/aarch64/asm.rs b/winch/codegen/src/isa/aarch64/asm.rs
index 2c6a668da..e5bb5ee17 100644
--- a/winch/codegen/src/isa/aarch64/asm.rs
+++ b/winch/codegen/src/isa/aarch64/asm.rs
@@ -315,9 +315,9 @@ impl Assembler {
         });
     }
 
-    pub fn mov_from_vec(&mut self, rn: Reg, rd: Reg, idx: u8, size: OperandSize) {
+    pub fn mov_from_vec(&mut self, rn: Reg, rd: WritableReg, idx: u8, size: OperandSize) {
         self.emit(Inst::MovFromVec {
-            rd: Writable::from_reg(rd.into()),
+            rd: rd.map(Into::into),
             rn: rn.into(),
             idx,
             size: size.into(),
@@ -342,10 +342,10 @@ impl Assembler {
     }
 
     /// Add across Vector.
-    pub fn addv(&mut self, rn: Reg, rd: Reg, size: VectorSize) {
+    pub fn addv(&mut self, rn: Reg, rd: WritableReg, size: VectorSize) {
         self.emit(Inst::VecLanes {
             op: VecLanesOp::Addv,
-            rd: Writable::from_reg(rd.into()),
+            rd: rd.map(Into::into),
             rn: rn.into(),
             size,
         });
@@ -540,7 +540,7 @@ impl Assembler {
     }
 
     /// Float round (ceil, trunc, floor) with two registers.
-    pub fn fround_rr(&mut self, rn: Reg, rd: Reg, mode: RoundingMode, size: OperandSize) {
+    pub fn fround_rr(&mut self, rn: Reg, rd: WritableReg, mode: RoundingMode, size: OperandSize) {
         let fpu_mode = match (mode, size) {
             (RoundingMode::Nearest, OperandSize::S32) => FpuRoundMode::Nearest32,
             (RoundingMode::Up, OperandSize::S32) => FpuRoundMode::Plus32,
@@ -687,11 +687,11 @@ impl Assembler {
     }
 
     // Population Count per byte.
-    pub fn cnt(&mut self, rd: Reg) {
+    pub fn cnt(&mut self, rd: WritableReg) {
         self.emit(Inst::VecMisc {
             op: VecMisc2::Cnt,
-            rd: Writable::from_reg(rd.into()),
-            rn: rd.into(),
+            rd: rd.map(Into::into),
+            rn: rd.to_reg().into(),
             size: VectorSize::Size8x8,
         });
     }
@@ -853,10 +853,10 @@ impl Assembler {
         });
     }
 
-    fn emit_fpu_round(&mut self, op: FpuRoundMode, rn: Reg, rd: Reg) {
+    fn emit_fpu_round(&mut self, op: FpuRoundMode, rn: Reg, rd: WritableReg) {
         self.emit(Inst::FpuRound {
             op: op,
-            rd: Writable::from_reg(rd.into()),
+            rd: rd.map(Into::into),
             rn: rn.into(),
         });
     }
diff --git a/winch/codegen/src/isa/aarch64/masm.rs b/winch/codegen/src/isa/aarch64/masm.rs
index dbc17b037..fa089b134 100644
--- a/winch/codegen/src/isa/aarch64/masm.rs
+++ b/winch/codegen/src/isa/aarch64/masm.rs
@@ -359,7 +359,8 @@ impl Masm for MacroAssembler {
         _fallback: F,
     ) {
         let src = context.pop_to_reg(self, None);
-        self.asm.fround_rr(src.into(), src.into(), mode, size);
+        self.asm
+            .fround_rr(src.into(), writable!(src.into()), mode, size);
         context.stack.push(src.into());
     }
 
@@ -459,9 +460,10 @@ impl Masm for MacroAssembler {
         let src = context.pop_to_reg(self, None);
         let tmp = regs::float_scratch();
         self.asm.mov_to_fpu(src.into(), writable!(tmp), size);
-        self.asm.cnt(tmp);
-        self.asm.addv(tmp, tmp, VectorSize::Size8x8);
-        self.asm.mov_from_vec(tmp, src.into(), 0, OperandSize::S8);
+        self.asm.cnt(writable!(tmp));
+        self.asm.addv(tmp, writable!(tmp), VectorSize::Size8x8);
+        self.asm
+            .mov_from_vec(tmp, writable!(src.into()), 0, OperandSize::S8);
         context.stack.push(src.into());
     }
 
diff --git a/winch/codegen/src/isa/x64/asm.rs b/winch/codegen/src/isa/x64/asm.rs
index ebec91d01..bcb0e7187 100644
--- a/winch/codegen/src/isa/x64/asm.rs
+++ b/winch/codegen/src/isa/x64/asm.rs
@@ -1224,7 +1224,13 @@ impl Assembler {
 
     /// Perform rounding operation on float register src and place results in
     /// float register dst.
-    pub fn xmm_rounds_rr(&mut self, src: Reg, dst: Reg, mode: RoundingMode, size: OperandSize) {
+    pub fn xmm_rounds_rr(
+        &mut self,
+        src: Reg,
+        dst: WritableReg,
+        mode: RoundingMode,
+        size: OperandSize,
+    ) {
         let op = match size {
             OperandSize::S32 => SseOpcode::Roundss,
             OperandSize::S64 => SseOpcode::Roundsd,
@@ -1242,7 +1248,7 @@ impl Assembler {
             op,
             src: XmmMemAligned::from(Xmm::from(src)),
             imm,
-            dst: dst.into(),
+            dst: dst.map(Into::into),
         })
     }
 
diff --git a/winch/codegen/src/isa/x64/masm.rs b/winch/codegen/src/isa/x64/masm.rs
index e42f39e62..f333f84ee 100644
--- a/winch/codegen/src/isa/x64/masm.rs
+++ b/winch/codegen/src/isa/x64/masm.rs
@@ -483,7 +483,8 @@ impl Masm for MacroAssembler {
     ) {
         if self.flags.has_sse41() {
             let src = context.pop_to_reg(self, None);
-            self.asm.xmm_rounds_rr(src.into(), src.into(), mode, size);
+            self.asm
+                .xmm_rounds_rr(src.into(), writable!(src.into()), mode, size);
             context.stack.push(src.into());
         } else {
             fallback(env, context, self);


Test patch:
diff --git a/crates/test-programs/src/lib.rs b/crates/test-programs/src/lib.rs
index bcb00225a..57e81dda1 100644
--- a/crates/test-programs/src/lib.rs
+++ b/crates/test-programs/src/lib.rs
@@ -8,8 +8,8 @@ wit_bindgen::generate!({
         package wasmtime:test;
 
         world test {
-            include wasi:cli/imports@0.2.1;
-            include wasi:http/imports@0.2.1;
+            include wasi:cli/imports@0.2.2;
+            include wasi:http/imports@0.2.2;
             include wasi:config/imports@0.2.0-draft;
             include wasi:keyvalue/imports@0.2.0-draft;
         }
@@ -31,17 +31,17 @@ pub mod proxy {
         default_bindings_module: "test_programs::proxy",
         pub_export_macro: true,
         with: {
-            "wasi:http/types@0.2.1": crate::wasi::http::types,
-            "wasi:http/outgoing-handler@0.2.1": crate::wasi::http::outgoing_handler,
-            "wasi:random/random@0.2.1": crate::wasi::random::random,
-            "wasi:io/error@0.2.1": crate::wasi::io::error,
-            "wasi:io/poll@0.2.1": crate::wasi::io::poll,
-            "wasi:io/streams@0.2.1": crate::wasi::io::streams,
-            "wasi:cli/stdout@0.2.1": crate::wasi::cli::stdout,
-            "wasi:cli/stderr@0.2.1": crate::wasi::cli::stderr,
-            "wasi:cli/stdin@0.2.1": crate::wasi::cli::stdin,
-            "wasi:clocks/monotonic-clock@0.2.1": crate::wasi::clocks::monotonic_clock,
-            "wasi:clocks/wall-clock@0.2.1": crate::wasi::clocks::wall_clock,
+            "wasi:http/types@0.2.2": crate::wasi::http::types,
+            "wasi:http/outgoing-handler@0.2.2": crate::wasi::http::outgoing_handler,
+            "wasi:random/random@0.2.2": crate::wasi::random::random,
+            "wasi:io/error@0.2.2": crate::wasi::io::error,
+            "wasi:io/poll@0.2.2": crate::wasi::io::poll,
+            "wasi:io/streams@0.2.2": crate::wasi::io::streams,
+            "wasi:cli/stdout@0.2.2": crate::wasi::cli::stdout,
+            "wasi:cli/stderr@0.2.2": crate::wasi::cli::stderr,
+            "wasi:cli/stdin@0.2.2": crate::wasi::cli::stdin,
+            "wasi:clocks/monotonic-clock@0.2.2": crate::wasi::clocks::monotonic_clock,
+            "wasi:clocks/wall-clock@0.2.2": crate::wasi::clocks::wall_clock,
         },
     });
 }
diff --git a/crates/wasi/wit/test.wit b/crates/wasi/wit/test.wit
index 14e531d08..4013722cd 100644
--- a/crates/wasi/wit/test.wit
+++ b/crates/wasi/wit/test.wit
@@ -1,13 +1,13 @@
 world test-reactor {
-  include wasi:cli/imports@0.2.1;
+  include wasi:cli/imports@0.2.2;
 
   export add-strings: func(s: list<string>) -> u32;
   export get-strings: func() -> list<string>;
 
-  use wasi:io/streams@0.2.1.{output-stream};
+  use wasi:io/streams@0.2.2.{output-stream};
 
   export write-strings-to: func(o: output-stream) -> result;
 
-  use wasi:filesystem/types@0.2.1.{descriptor-stat};
+  use wasi:filesystem/types@0.2.2.{descriptor-stat};
   export pass-an-imported-record: func(d: descriptor-stat) -> string;
 }
diff --git a/tests/all/pooling_allocator.rs b/tests/all/pooling_allocator.rs
index 4bf8e0d4c..fc6e98da7 100644
--- a/tests/all/pooling_allocator.rs
+++ b/tests/all/pooling_allocator.rs
@@ -1215,3 +1215,45 @@ fn decommit_batching() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+fn tricky_empty_table_with_empty_virtual_memory_alloc() -> Result<()> {
+    // Configure the pooling allocator to have no access to virtual memory, e.g.
+    // no table elements but a single table. This should technically support a
+    // single empty table being allocated into it but virtual memory isn't
+    // actually allocated here.
+    let mut cfg = PoolingAllocationConfig::default();
+    cfg.table_elements(0);
+    cfg.total_memories(0);
+    cfg.total_tables(1);
+    cfg.total_stacks(0);
+    cfg.total_core_instances(1);
+    cfg.max_memory_size(0);
+
+    let mut c = Config::new();
+    c.allocation_strategy(InstanceAllocationStrategy::Pooling(cfg));
+
+    // Disable lazy init to actually try to get this to do something interesting
+    // at runtime.
+    c.table_lazy_init(false);
+
+    let engine = Engine::new(&c)?;
+
+    // This module has a single empty table, with a single empty element
+    // segment. Nothing actually goes wrong here, it should instantiate
+    // successfully. Along the way though the empty mmap above will get viewed
+    // as an array-of-pointers, so everything internally should all line up to
+    // work ok.
+    let module = Module::new(
+        &engine,
+        r#"
+(module
+    (table 0 funcref)
+    (elem (i32.const 0) func)
+)
+"#,
+    )?;
+    let mut store = Store::new(&engine, ());
+    Instance::new(&mut store, &module, &[])?;
+    Ok(())
+}