Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions design/mvp/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,10 @@ Notes:
`none` case of an optional immediate.)
* 🔧 for fixed-sized lists the length of the list must be larger than 0 to pass
validation.
* Validation of each `defvaltype` requires every resolved AST node (after
despecialization) to satisfy the `elem_size` bound in
[Element Size](CanonicalABI.md#element-size). This is a static validation
error, not a runtime trap.


## Canonical Definitions
Expand Down
14 changes: 14 additions & 0 deletions design/mvp/CanonicalABI.md
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,17 @@ def elem_size_flags(labels):
return 4
```

Validation of each `defvaltype` requires that every node `u` in the resolved
structural AST — after `despecialize`, including implicit nodes such as `map`'s
key/value record — satisfies `elem_size(u, ptr_type) ≤ 2^28−1` for both
`ptr_type ∈ {i32, i64}`. This is a static validation error, not a runtime trap.
The bound is the same number as `MAX_LIST_BYTE_LENGTH` / `MAX_STRING_BYTE_LENGTH`.
`elem_size` is unbounded-integer math; fixed-width implementations must reject
overflow rather than wrap. Checking only the root type is not enough:
`map<u8, list<u8, 2^28−1>>` has a small list header but an oversized pair record.
Incremental or memoized checking is allowed only if every resolved node of that
definition is still covered.

## Loading

The `load` function defines how to read a value of a given value type `t`
Expand Down Expand Up @@ -3594,6 +3605,9 @@ performed for a component. These are defined as:
* `lift(T)`
* requires `realloc` if `T` contains a `list` or `string`

Value types used by `lift`/`lower` are already rejected at `defvaltype`
definition if they exceed the [Element Size](#element-size) bound, so lift and
lower may assume static layouts fit.

### `canon lift`

Expand Down
9 changes: 9 additions & 0 deletions design/mvp/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,15 @@ is a central part of validation and, e.g., occurs when validating that the
`with` arguments of an [`instantiate`](#instance-definitions) expression are
type-compatible with the `import`s of the component being instantiated.

##### Maximum static value type size

Each `defvaltype` is rejected unless every node of its resolved structural AST
(after despecialization) has `elem_size` at most `2^28 − 1` for both `i32` and
`i64` pointer types. Checking only the root is not enough: wrappers such as
`map`, `option`, `stream`, and `future` can have a small header while a nested
node exceeds the bound. This is a static validation error. See
[Element Size](CanonicalABI.md#element-size).

To incrementally describe how type-checking works, we'll start by asking how
*type equality* works for non-resource, non-handle, local type definitions and
build up from there.
Expand Down
1 change: 1 addition & 0 deletions test/nyi.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# See README.md
./validation/max-value-size.wast
./async/during-sync-call-may-block-if-other-ready-threads.wast
./async/during-sync-call-no-exclusive-resume.wast
./async/during-sync-call-no-sibling-resume.wast
92 changes: 92 additions & 0 deletions test/validation/max-value-size.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
;; Validation rejects defvaltypes whose resolved structural AST exceeds
;; 2^28 - 1 bytes. See CanonicalABI.md#element-size.

;; valid boundaries

(component
(type (list u8 268435455))
)

(component
(type (list u64 33554431))
)

(component
(type (list string 16777215))
)

;; valid map despecialization (pair record is well under the bound)

(component
(type (map u8 (list u8 4)))
)

;; single fixed list just over the limit

(assert_invalid
(component (type (list u8 268435456)))
"exceeds maximum byte size")

;; fixed list whose product exceeds MAX

(assert_invalid
(component (type (list u64 33554432)))
"exceeds maximum byte size")

;; u32 wrap class: real byte size is 2^32 but naive u32 multiply wraps to 0

(assert_invalid
(component (type (list u64 536870912)))
"exceeds maximum byte size")

;; compound sum exceeds MAX

(assert_invalid
(component
(type (tuple (list u8 268435455) (list u8 1))))
"exceeds maximum byte size")

(assert_invalid
(component
(type (record
(field "a" (list u8 134217728))
(field "b" (list u8 134217728)))))
"exceeds maximum byte size")

;; map despecialization: inner list valid alone, pair record is MAX + 1

(assert_invalid
(component (type (map u8 (list u8 268435455))))
"exceeds maximum byte size")

;; nested fixed list

(assert_invalid
(component (type (list (list u8 268435455) 2)))
"exceeds maximum byte size")

;; pointer-width-sensitive rejection (passes i32, fails i64)

(assert_invalid
(component (type (list string 16777216)))
"exceeds maximum byte size")

;; nested map despecialization inside option and record

(assert_invalid
(component (type (option (map u8 (list u8 268435455)))))
"exceeds maximum byte size")

(assert_invalid
(component (type (record (field "m" (map u8 (list u8 268435455))))))
"exceeds maximum byte size")

;; stream/future handle size is 4; payload must still be checked

(assert_invalid
(component (type (stream (list u8 268435455))))
"exceeds maximum byte size")

(assert_invalid
(component (type (future (list u8 268435455))))
"exceeds maximum byte size")