diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md index f8aa2772..33029e18 100644 --- a/design/mvp/Binary.md +++ b/design/mvp/Binary.md @@ -287,6 +287,7 @@ 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 requires that, for every `defvaltype` `t`, `elem_size(t, 'i64')` is less than 228, as defined by the [Canonical ABI](CanonicalABI.md#element-size). ## Canonical Definitions diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md index 621296b5..febc1b23 100644 --- a/design/mvp/CanonicalABI.md +++ b/design/mvp/CanonicalABI.md @@ -2306,7 +2306,10 @@ byte size be a static property of the type instead of attempting to use a variable-length element-encoding scheme both simplifies the implementation and maps well to languages which represent `list`s as random-access arrays. Empty types, such as records with no fields, are not permitted, to avoid -complications in source languages. +complications in source languages. To prevent integer overflow in obscure corner +cases, component validation rules require that for every value type `t` defined +by a component, `elem_size(t, 'i64')` is less than 228 (the same +upper bound as `MAX_LIST_BYTE_LENGTH`). ```python def elem_size(t, ptr_type): match despecialize(t): @@ -3594,6 +3597,8 @@ 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. ### `canon lift` diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md index 28d51969..766c59ce 100644 --- a/design/mvp/Explainer.md +++ b/design/mvp/Explainer.md @@ -658,6 +658,11 @@ where bind-id(X) parses '(' sort ? Y ')' when X parses '(' sort Y ')' Because there is nothing in this type grammar analogous to the [gc] proposal's [`rectype`], none of these types are recursive. +To prevent integer overflow in obscure corner cases, as an extra validation +requirement, `defvaltype`s may not be equal or greater than 228 bytes +when serialized into linear memory according to the `i64` ABI definition of +[Element Size](CanonicalABI.md#element-size). + #### Fundamental value types The value types in `valtype` can be broken into two categories: *fundamental* diff --git a/test/nyi.txt b/test/nyi.txt index ffb2d182..8f3e4250 100644 --- a/test/nyi.txt +++ b/test/nyi.txt @@ -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 diff --git a/test/validation/max-value-size.wast b/test/validation/max-value-size.wast new file mode 100644 index 00000000..d26f99b2 --- /dev/null +++ b/test/validation/max-value-size.wast @@ -0,0 +1,65 @@ +;; Validation requires elem_size(t, i64) < 2^28 for every defvaltype t. +;; See CanonicalABI.md#element-size. + +;; valid boundaries (single component) + +(component + (type (list u8 268435455)) + (type (list u64 33554431)) + (type (list string 16777215)) + (type (map u8 (list u8 4))) + (type (tuple (list u8 268435454) (list u8 1))) + (type (record + (field "a" (list u8 134217727)) + (field "b" (list u8 134217728)))) + (type (list (list u8 134217727) 2)) + (type (map u8 (list u8 268435455))) + (type (option (map u8 (list u8 268435455)))) + (type (record (field "m" (map u8 (list u8 268435455))))) + (type (stream (list u8 268435455))) + (type (future (list u8 268435455))) +) + +;; 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") + +;; 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")