Skip to content

Commit d7329d9

Browse files
committed
feat(random): make get-random-bytes fallible
get-random-bytes and get-insecure-random-bytes accept a u64 length with no way to signal failure, allowing guests to request up to 2^64-1 bytes and forcing hosts to allocate unbounded memory or hard-trap. Add an error enum with a too-many-bytes case and change both functions to return result<list<u8>, error>. Add max-random-bytes-length and max-insecure-random-bytes-length query functions so guests can check limits upfront. Hosts MUST support at least 4096 bytes. The error type is defined in the random interface and reused by insecure via use. get-random-u64 and get-insecure-random-u64 are unchanged. Part of #888
1 parent bef9091 commit d7329d9

File tree

5 files changed

+105
-16
lines changed

5 files changed

+105
-16
lines changed

proposals/cli/wit-0.3.0-draft/deps.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ sha512 = "f08a2828b88fc6ddea935af584531c484ad4a7a5f30340265e11e91b2bfe0f81e74a66
55

66
[filesystem]
77
path = "../../filesystem/wit-0.3.0-draft"
8-
sha256 = "184861e98785957311bfaab242cdf9e66a9ecca11fe2c493b840c461b2361088"
9-
sha512 = "50fa8801fc0a2c1ecfa3cea52af57836f98a12bb0a264439c13bbdcc0e269b3b37ade38b903d6ce10594d1f585d02ef993f3f769c4cddeebdfc00bf93734ed25"
8+
sha256 = "8808ea3adfbc1a025d649b82ddf4f38232ca4377100cfe671d80d5ee37fa3147"
9+
sha512 = "19f4eb8fa62e96ba37b3ea231af6a3bc396c28f82935018a3322441321936b34fb0e44360b378145fcb681d9fea810745969d8baab02ae6017be1784be8abe45"
1010

1111
[random]
1212
path = "../../random/wit-0.3.0-draft"
13-
sha256 = "5794796c909d6656fcbae6bed28265210ca57308a624119ac0a472326a75aa8f"
14-
sha512 = "812ce57aa13ff3128779d41f4dad50714365e4f9cfd2e1b13458a885fa65da05e409f145deefa25c4a82e0e301a41e2e6572705b35752dc33908d565a73a2e9c"
13+
sha256 = "329785794587f27cc531d19e23fe872237f052d7d839b29ae2288db5ae9f0533"
14+
sha512 = "691a26b30ce4fdfce070d0a9ccfe8b4f998b8dca6f58a7e22298457a8e0d05eba2f5fd38aa19879cc25bcc5f73a99358b33ae2690dde4e1dfea80fc841b1d69b"
1515

1616
[sockets]
1717
path = "../../sockets/wit-0.3.0-draft"
18-
sha256 = "985821e86f2643d90b7a100420a44a5a60a6838adcf76fdb90a66255e3926dde"
19-
sha512 = "9ba1e9456c0dc02800ba738acd382a4113103bed72127396546c5f0ad3d38dd5c8e077443bd508b15f86f6095706907e9cb258cccab37c273b4c597a238987e7"
18+
sha256 = "0be70fab90ec1d62e620f37f8fc55397222ceb68ca8387eb0503df7173782634"
19+
sha512 = "997e336258dd3d8d1bf1b27463e77d1dc2160eb13fff899c93b446973f5efbc59448a23279b60568679c08e006a20cc88769ec74d6d30baa4e7a17df0bdb2c30"

proposals/http/wit-0.3.0-draft/deps.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ sha256 = "888647625fec3eaaf276cb884e426bc32bfa79ced22955f10eb239df74c8550c"
1010
sha512 = "f08a2828b88fc6ddea935af584531c484ad4a7a5f30340265e11e91b2bfe0f81e74a660a512f72e5197d60278feccc00534833ebd73868e801859dd31a61bdbb"
1111

1212
[filesystem]
13-
sha256 = "184861e98785957311bfaab242cdf9e66a9ecca11fe2c493b840c461b2361088"
14-
sha512 = "50fa8801fc0a2c1ecfa3cea52af57836f98a12bb0a264439c13bbdcc0e269b3b37ade38b903d6ce10594d1f585d02ef993f3f769c4cddeebdfc00bf93734ed25"
13+
sha256 = "8808ea3adfbc1a025d649b82ddf4f38232ca4377100cfe671d80d5ee37fa3147"
14+
sha512 = "19f4eb8fa62e96ba37b3ea231af6a3bc396c28f82935018a3322441321936b34fb0e44360b378145fcb681d9fea810745969d8baab02ae6017be1784be8abe45"
1515

1616
[random]
17-
sha256 = "5794796c909d6656fcbae6bed28265210ca57308a624119ac0a472326a75aa8f"
18-
sha512 = "812ce57aa13ff3128779d41f4dad50714365e4f9cfd2e1b13458a885fa65da05e409f145deefa25c4a82e0e301a41e2e6572705b35752dc33908d565a73a2e9c"
17+
sha256 = "329785794587f27cc531d19e23fe872237f052d7d839b29ae2288db5ae9f0533"
18+
sha512 = "691a26b30ce4fdfce070d0a9ccfe8b4f998b8dca6f58a7e22298457a8e0d05eba2f5fd38aa19879cc25bcc5f73a99358b33ae2690dde4e1dfea80fc841b1d69b"
1919

2020
[sockets]
21-
sha256 = "985821e86f2643d90b7a100420a44a5a60a6838adcf76fdb90a66255e3926dde"
22-
sha512 = "9ba1e9456c0dc02800ba738acd382a4113103bed72127396546c5f0ad3d38dd5c8e077443bd508b15f86f6095706907e9cb258cccab37c273b4c597a238987e7"
21+
sha256 = "0be70fab90ec1d62e620f37f8fc55397222ceb68ca8387eb0503df7173782634"
22+
sha512 = "997e336258dd3d8d1bf1b27463e77d1dc2160eb13fff899c93b446973f5efbc59448a23279b60568679c08e006a20cc88769ec74d6d30baa4e7a17df0bdb2c30"

proposals/random/README.md

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@ The primary goals of WASI Random are:
5050

5151
### Non-goals
5252

53-
WASI Random is not aiming to allow programs to handle errors or to query for
53+
In WASIp2, WASI Random does not allow programs to handle errors or to query for
5454
availability. It always succeeds (though on platforms where randomness is
55-
unavailable, programs may fail to be instantiated or may trap).
55+
unavailable, programs may fail to be instantiated or may trap). In WASIp3,
56+
`get-random-bytes` and `get-insecure-random-bytes` return `result` types so that
57+
hosts can reject requests that exceed a maximum length (see
58+
[Resource exhaustion](#resource-exhaustion) below).
5659

5760
WASI Random is not aiming to be a full DRBG API. Such an API could be
5861
considered in WASI, but it should be a separate proposal.
@@ -106,6 +109,28 @@ another function is available which returns the same data but as a
106109
let data: u64 = get_random_u64();
107110
```
108111

112+
#### Requesting large byte sequences safely (WASIp3)
113+
114+
Before requesting a large number of random bytes, query the host's limit:
115+
116+
```rust
117+
let max_len: u64 = max_random_bytes_length();
118+
let desired: u64 = your_own_code_to_decide_how_many_bytes_you_want();
119+
120+
if desired <= max_len {
121+
let bytes: Vec<u8> = get_random_bytes(desired).unwrap();
122+
} else {
123+
// Request in chunks of max_len
124+
let mut all_bytes = Vec::new();
125+
let mut remaining = desired;
126+
while remaining > 0 {
127+
let chunk_len = remaining.min(max_len);
128+
all_bytes.extend(get_random_bytes(chunk_len).unwrap());
129+
remaining -= chunk_len;
130+
}
131+
}
132+
```
133+
109134
#### Insecure API: Hash-map DoS protection
110135

111136
Return a pair of u64's that can be used to initialize a hash implementation:
@@ -171,6 +196,31 @@ their bits of security, and it doesn't seem desirable to require wasm engines to
171196
run their own CSPRNG on a platform which already has one, so for now, the API
172197
does not specify a specific number.
173198

199+
### Resource exhaustion
200+
201+
In WASIp2, `get-random-bytes` and `get-insecure-random-bytes` accept a `u64`
202+
length and return `list<u8>` with no way to signal failure. A guest can request
203+
up to 2^64-1 bytes, forcing hosts to either allocate unbounded memory or
204+
hard-trap. This was reported as [GHSA-852m-cvvp-9p4w].
205+
206+
WASIp3 addresses this in two ways:
207+
208+
1. **`result` return types**: Both functions now return `result<list<u8>, error>`
209+
where `error` is an enum with a `too-many-bytes` case. This allows hosts to
210+
reject oversized requests gracefully instead of trapping.
211+
212+
2. **Max-length query functions**: `max-random-bytes-length` and
213+
`max-insecure-random-bytes-length` let guests discover the host's limit
214+
before making a request. Hosts MUST support at least 4096 (4 KiB) bytes.
215+
216+
The `error` enum is defined once in the `random` interface and reused by
217+
`insecure` via `use random.{error}`, keeping the error type consistent.
218+
219+
The `get-random-u64` and `get-insecure-random-u64` functions are unchanged since
220+
they always return exactly 8 bytes, posing no resource exhaustion risk.
221+
222+
[GHSA-852m-cvvp-9p4w]: https://github.com/WebAssembly/WASI/security/advisories/GHSA-852m-cvvp-9p4w
223+
174224
### Why is insecure-random a fixed-sized return value?
175225

176226
This limits the amount of data that can be obtained through it. Since it's

proposals/random/wit-0.3.0-draft/insecure.wit

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ package wasi:random@0.3.0-rc-2026-02-09;
55
/// Windows.
66
@since(version = 0.3.0-rc-2026-02-09)
77
interface insecure {
8+
@since(version = 0.3.0-rc-2026-02-09)
9+
use random.{error};
10+
11+
/// Return the maximum number of bytes that can be requested in a single
12+
/// call to `get-insecure-random-bytes`.
13+
///
14+
/// Hosts MUST support a value of at least 4096 (4 KiB). Guests SHOULD
15+
/// query this limit before requesting large byte sequences to avoid
16+
/// `too-many-bytes` errors.
17+
@since(version = 0.3.0-rc-2026-02-09)
18+
max-insecure-random-bytes-length: func() -> u64;
19+
820
/// Return `len` insecure pseudo-random bytes.
921
///
1022
/// This function is not cryptographically secure. Do not use it for
@@ -13,8 +25,13 @@ interface insecure {
1325
/// There are no requirements on the values of the returned bytes, however
1426
/// implementations are encouraged to return evenly distributed values with
1527
/// a long period.
28+
///
29+
/// # Errors
30+
///
31+
/// Returns `error::too-many-bytes` if `len` exceeds the value returned by
32+
/// `max-insecure-random-bytes-length`.
1633
@since(version = 0.3.0-rc-2026-02-09)
17-
get-insecure-random-bytes: func(len: u64) -> list<u8>;
34+
get-insecure-random-bytes: func(len: u64) -> result<list<u8>, error>;
1835

1936
/// Return an insecure pseudo-random `u64` value.
2037
///

proposals/random/wit-0.3.0-draft/random.wit

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ package wasi:random@0.3.0-rc-2026-02-09;
55
/// Windows.
66
@since(version = 0.3.0-rc-2026-02-09)
77
interface random {
8+
/// An error type for random byte generation.
9+
@since(version = 0.3.0-rc-2026-02-09)
10+
enum error {
11+
/// The requested number of bytes exceeds the host's supported limit.
12+
/// Use `max-random-bytes-length` to query the maximum supported length.
13+
too-many-bytes,
14+
}
15+
16+
/// Return the maximum number of bytes that can be requested in a single
17+
/// call to `get-random-bytes`.
18+
///
19+
/// Hosts MUST support a value of at least 4096 (4 KiB). Guests SHOULD
20+
/// query this limit before requesting large byte sequences to avoid
21+
/// `too-many-bytes` errors.
22+
@since(version = 0.3.0-rc-2026-02-09)
23+
max-random-bytes-length: func() -> u64;
24+
825
/// Return `len` cryptographically-secure random or pseudo-random bytes.
926
///
1027
/// This function must produce data at least as cryptographically secure and
@@ -17,8 +34,13 @@ interface random {
1734
/// This function must always return fresh data. Deterministic environments
1835
/// must omit this function, rather than implementing it with deterministic
1936
/// data.
37+
///
38+
/// # Errors
39+
///
40+
/// Returns `error::too-many-bytes` if `len` exceeds the value returned by
41+
/// `max-random-bytes-length`.
2042
@since(version = 0.3.0-rc-2026-02-09)
21-
get-random-bytes: func(len: u64) -> list<u8>;
43+
get-random-bytes: func(len: u64) -> result<list<u8>, error>;
2244

2345
/// Return a cryptographically-secure random or pseudo-random `u64` value.
2446
///

0 commit comments

Comments
 (0)