Skip to content

Commit 47195f1

Browse files
authored
Model constraints on StringNew children more precisely (#7193)
The stringref spec has a bug where the string.new... instructions that take array reference parameters do not have the proper type annotations, which means IRBuilder does not have a single type that it knows the array reference must have. We previously fudged this by using `arrayref` as the type constraint, but the fuzzer was able to generate a valid test case that was parsed into invalid IR due to this imprecision. Fix the bug by adding special constraint types for "any i8 array reference" and "any i16 array reference" just for use with the offending string.new instructions. Fixes #7191.
1 parent 8d0f662 commit 47195f1

File tree

3 files changed

+326
-19
lines changed

3 files changed

+326
-19
lines changed

src/ir/child-typer.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,18 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
9191
self().noteAnyTupleType(childp, arity);
9292
}
9393

94+
// Used only for string.new_lossy_utf8_array to work around a missing type
95+
// annotation in the stringref spec.
96+
void noteAnyI8ArrayReferenceType(Expression** childp) {
97+
self().noteAnyI8ArrayReferenceType(childp);
98+
}
99+
100+
// Used only for string.new_wtf16_array to work around a missing type
101+
// annotation in the stringref spec.
102+
void noteAnyI16ArrayReferenceType(Expression** childp) {
103+
self().noteAnyI16ArrayReferenceType(childp);
104+
}
105+
94106
Type getLabelType(Name label) { return self().getLabelType(label); }
95107

96108
void visitNop(Nop* curr) {}
@@ -992,15 +1004,15 @@ template<typename Subtype> struct ChildTyper : OverriddenVisitor<Subtype> {
9921004
WASM_UNREACHABLE("unexpected op");
9931005
}
9941006

995-
void visitStringNew(StringNew* curr,
996-
std::optional<HeapType> ht = std::nullopt) {
1007+
void visitStringNew(StringNew* curr) {
9971008
switch (curr->op) {
9981009
case StringNewLossyUTF8Array:
1010+
noteAnyI8ArrayReferenceType(&curr->ref);
1011+
note(&curr->start, Type::i32);
1012+
note(&curr->end, Type::i32);
1013+
return;
9991014
case StringNewWTF16Array:
1000-
if (!ht) {
1001-
ht = curr->ref->type.getHeapType();
1002-
}
1003-
note(&curr->ref, Type(*ht, Nullable));
1015+
noteAnyI16ArrayReferenceType(&curr->ref);
10041016
note(&curr->start, Type::i32);
10051017
note(&curr->end, Type::i32);
10061018
return;

src/wasm/wasm-ir-builder.cpp

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,16 @@ struct IRBuilder::ChildPopper
292292
size_t arity;
293293
};
294294

295-
struct Constraint : std::variant<Subtype, AnyType, AnyReference, AnyTuple> {
295+
struct AnyI8ArrayReference {};
296+
297+
struct AnyI16ArrayReference {};
298+
299+
struct Constraint : std::variant<Subtype,
300+
AnyType,
301+
AnyReference,
302+
AnyTuple,
303+
AnyI8ArrayReference,
304+
AnyI16ArrayReference> {
296305
std::optional<Type> getSubtype() const {
297306
if (auto* subtype = std::get_if<Subtype>(this)) {
298307
return subtype->bound;
@@ -301,6 +310,12 @@ struct IRBuilder::ChildPopper
301310
}
302311
bool isAnyType() const { return std::get_if<AnyType>(this); }
303312
bool isAnyReference() const { return std::get_if<AnyReference>(this); }
313+
bool isAnyI8ArrayReference() const {
314+
return std::get_if<AnyI8ArrayReference>(this);
315+
}
316+
bool isAnyI16ArrayReference() const {
317+
return std::get_if<AnyI16ArrayReference>(this);
318+
}
304319
std::optional<size_t> getAnyTuple() const {
305320
if (auto* tuple = std::get_if<AnyTuple>(this)) {
306321
return tuple->arity;
@@ -356,6 +371,14 @@ struct IRBuilder::ChildPopper
356371
children.push_back({childp, {AnyTuple{arity}}});
357372
}
358373

374+
void noteAnyI8ArrayReferenceType(Expression** childp) {
375+
children.push_back({childp, {AnyI8ArrayReference{}}});
376+
}
377+
378+
void noteAnyI16ArrayReferenceType(Expression** childp) {
379+
children.push_back({childp, {AnyI16ArrayReference{}}});
380+
}
381+
359382
Type getLabelType(Name label) {
360383
WASM_UNREACHABLE("labels should be explicitly provided");
361384
};
@@ -455,6 +478,26 @@ struct IRBuilder::ChildPopper
455478
needUnreachableFallback = true;
456479
break;
457480
}
481+
} else if (constraint.isAnyI8ArrayReference()) {
482+
bool isI8Array =
483+
type.isRef() && type.getHeapType().isArray() &&
484+
type.getHeapType().getArray().element.packedType == Field::i8;
485+
bool isNone =
486+
type.isRef() && type.getHeapType().isMaybeShared(HeapType::none);
487+
if (!isI8Array && !isNone && type != Type::unreachable) {
488+
needUnreachableFallback = true;
489+
break;
490+
}
491+
} else if (constraint.isAnyI16ArrayReference()) {
492+
bool isI16Array =
493+
type.isRef() && type.getHeapType().isArray() &&
494+
type.getHeapType().getArray().element.packedType == Field::i16;
495+
bool isNone =
496+
type.isRef() && type.getHeapType().isMaybeShared(HeapType::none);
497+
if (!isI16Array && !isNone && type != Type::unreachable) {
498+
needUnreachableFallback = true;
499+
break;
500+
}
458501
} else {
459502
WASM_UNREACHABLE("unexpected constraint");
460503
}
@@ -601,13 +644,6 @@ struct IRBuilder::ChildPopper
601644
return popConstrainedChildren(children);
602645
}
603646

604-
Result<> visitStringNew(StringNew* curr,
605-
std::optional<HeapType> ht = std::nullopt) {
606-
std::vector<Child> children;
607-
ConstraintCollector{builder, children}.visitStringNew(curr, ht);
608-
return popConstrainedChildren(children);
609-
}
610-
611647
Result<> visitStringEncode(StringEncode* curr,
612648
std::optional<HeapType> ht = std::nullopt) {
613649
std::vector<Child> children;
@@ -1951,11 +1987,7 @@ Result<> IRBuilder::makeStringNew(StringNewOp op) {
19511987
push(builder.makeStringNew(op, curr.ref));
19521988
return Ok{};
19531989
}
1954-
// There's no type annotation on these instructions due to a bug in the
1955-
// stringref proposal, so we just fudge it and pass `array` instead of a
1956-
// defined heap type. This will allow us to pop a child with an invalid
1957-
// array type, but that's just too bad.
1958-
CHECK_ERR(ChildPopper{*this}.visitStringNew(&curr, HeapType::array));
1990+
CHECK_ERR(visitStringNew(&curr));
19591991
push(builder.makeStringNew(op, curr.ref, curr.start, curr.end));
19601992
return Ok{};
19611993
}

0 commit comments

Comments
 (0)