Skip to content

Commit 64fee0e

Browse files
authored
Fix requiring memory for lowered functions (#1150)
Currently component validation erroneously allows lowering a component function with lists/strings in parameters without a `memory` canonical option, despite this being required for the function. This commit fixes the issue and additionally refactors the code by renaming `import` to `is_lower` and renaming `requires_realloc` to `contains_ptr`.
1 parent 6684a5b commit 64fee0e

File tree

3 files changed

+54
-28
lines changed

3 files changed

+54
-28
lines changed

crates/wasmparser/src/readers/component/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ impl PrimitiveValType {
186186
})
187187
}
188188

189-
pub(crate) fn requires_realloc(&self) -> bool {
189+
pub(crate) fn contains_ptr(&self) -> bool {
190190
matches!(self, Self::String)
191191
}
192192

crates/wasmparser/src/validator/types.rs

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ pub enum ComponentValType {
322322
}
323323

324324
impl ComponentValType {
325-
pub(crate) fn requires_realloc(&self, types: &TypeList) -> bool {
325+
pub(crate) fn contains_ptr(&self, types: &TypeList) -> bool {
326326
match self {
327-
ComponentValType::Primitive(ty) => ty.requires_realloc(),
328-
ComponentValType::Type(ty) => types[*ty].unwrap_defined().requires_realloc(types),
327+
ComponentValType::Primitive(ty) => ty.contains_ptr(),
328+
ComponentValType::Type(ty) => types[*ty].unwrap_defined().contains_ptr(types),
329329
}
330330
}
331331

@@ -660,14 +660,24 @@ pub struct ComponentFuncType {
660660
impl ComponentFuncType {
661661
/// Lowers the component function type to core parameter and result types for the
662662
/// canonical ABI.
663-
pub(crate) fn lower(&self, types: &TypeList, import: bool) -> LoweringInfo {
663+
pub(crate) fn lower(&self, types: &TypeList, is_lower: bool) -> LoweringInfo {
664664
let mut info = LoweringInfo::default();
665665

666666
for (_, ty) in self.params.iter() {
667-
// When `import` is false, it means we're lifting a core function,
668-
// check if the parameters needs realloc
669-
if !import && !info.requires_realloc {
670-
info.requires_realloc = ty.requires_realloc(types);
667+
// Check to see if `ty` has a pointer somewhere in it, needed for
668+
// any type that transitively contains either a string or a list.
669+
// In this situation lowered functions must specify `memory`, and
670+
// lifted functions must specify `realloc` as well. Lifted functions
671+
// gain their memory requirement through the final clause of this
672+
// function.
673+
if is_lower {
674+
if !info.requires_memory {
675+
info.requires_memory = ty.contains_ptr(types);
676+
}
677+
} else {
678+
if !info.requires_realloc {
679+
info.requires_realloc = ty.contains_ptr(types);
680+
}
671681
}
672682

673683
if !ty.push_wasm_types(types, &mut info.params) {
@@ -679,25 +689,27 @@ impl ComponentFuncType {
679689
info.requires_memory = true;
680690

681691
// We need realloc as well when lifting a function
682-
if !import {
692+
if !is_lower {
683693
info.requires_realloc = true;
684694
}
685695
break;
686696
}
687697
}
688698

689699
for (_, ty) in self.results.iter() {
690-
// When `import` is true, it means we're lowering a component function,
691-
// check if the result needs realloc
692-
if import && !info.requires_realloc {
693-
info.requires_realloc = ty.requires_realloc(types);
700+
// Results of lowered functions that contains pointers must be
701+
// allocated by the callee meaning that realloc is required.
702+
// Results of lifted function are allocated by the guest which
703+
// means that no realloc option is necessary.
704+
if is_lower && !info.requires_realloc {
705+
info.requires_realloc = ty.contains_ptr(types);
694706
}
695707

696708
if !ty.push_wasm_types(types, &mut info.results) {
697709
// Too many results to return directly, either a retptr parameter will be used (import)
698710
// or a single pointer will be returned (export)
699711
info.results.clear();
700-
if import {
712+
if is_lower {
701713
info.params.max = MAX_LOWERED_TYPES;
702714
assert!(info.params.push(ValType::I32));
703715
} else {
@@ -795,23 +807,22 @@ pub enum ComponentDefinedType {
795807
}
796808

797809
impl ComponentDefinedType {
798-
pub(crate) fn requires_realloc(&self, types: &TypeList) -> bool {
810+
pub(crate) fn contains_ptr(&self, types: &TypeList) -> bool {
799811
match self {
800-
Self::Primitive(ty) => ty.requires_realloc(),
801-
Self::Record(r) => r.fields.values().any(|ty| ty.requires_realloc(types)),
802-
Self::Variant(v) => v.cases.values().any(|case| {
803-
case.ty
804-
.map(|ty| ty.requires_realloc(types))
805-
.unwrap_or(false)
806-
}),
812+
Self::Primitive(ty) => ty.contains_ptr(),
813+
Self::Record(r) => r.fields.values().any(|ty| ty.contains_ptr(types)),
814+
Self::Variant(v) => v
815+
.cases
816+
.values()
817+
.any(|case| case.ty.map(|ty| ty.contains_ptr(types)).unwrap_or(false)),
807818
Self::List(_) => true,
808-
Self::Tuple(t) => t.types.iter().any(|ty| ty.requires_realloc(types)),
809-
Self::Union(u) => u.types.iter().any(|ty| ty.requires_realloc(types)),
819+
Self::Tuple(t) => t.types.iter().any(|ty| ty.contains_ptr(types)),
820+
Self::Union(u) => u.types.iter().any(|ty| ty.contains_ptr(types)),
810821
Self::Flags(_) | Self::Enum(_) | Self::Own(_) | Self::Borrow(_) => false,
811-
Self::Option(ty) => ty.requires_realloc(types),
822+
Self::Option(ty) => ty.contains_ptr(types),
812823
Self::Result { ok, err } => {
813-
ok.map(|ty| ty.requires_realloc(types)).unwrap_or(false)
814-
|| err.map(|ty| ty.requires_realloc(types)).unwrap_or(false)
824+
ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
825+
|| err.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
815826
}
816827
}
817828
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(assert_invalid
2+
(component
3+
(import "f" (func $f (param "x" (list u8))))
4+
(core func $f (canon lower (func $f)
5+
))
6+
)
7+
"canonical option `memory` is required")
8+
9+
(assert_invalid
10+
(component
11+
(import "f" (func $f (result (list u8))))
12+
(core func $f (canon lower (func $f)
13+
))
14+
)
15+
"canonical option `memory` is required")

0 commit comments

Comments
 (0)