Skip to content

Commit

Permalink
Validate that allocations produce non-nullable references (WebAssembl…
Browse files Browse the repository at this point in the history
…y#7346)

For `struct.new`, `array.new`, `ref.func`, `ref.i31`, `cont.new`, and
`cont.bind`, check in the validator that the result type is a
non-nullable reference. This ensures that we have the most precise
possible type information for these instructions. The generic stale type
checker does not check this because finalizing these expressions does
not change their types.
  • Loading branch information
tlively authored Mar 5, 2025
1 parent dc055a8 commit bc47696
Showing 1 changed file with 61 additions and 12 deletions.
73 changes: 61 additions & 12 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2355,6 +2355,11 @@ void FunctionValidator::visitRefFunc(RefFunc* curr) {
shouldBeTrue(!getFunction() || getModule()->features.hasReferenceTypes(),
curr,
"ref.func requires reference-types [--enable-reference-types]");
if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"ref.func should have a non-nullable reference type")) {
return;
}
if (!info.validateGlobally) {
return;
}
Expand All @@ -2372,7 +2377,6 @@ void FunctionValidator::visitRefFunc(RefFunc* curr) {
// API (for now those users create the type with funcref). This also needs to
// be fixed in LegalizeJSInterface and FuncCastEmulation and other places that
// update function types.
// TODO: check for non-nullability
}

void FunctionValidator::visitRefEq(RefEq* curr) {
Expand Down Expand Up @@ -2843,16 +2847,33 @@ void FunctionValidator::visitCallRef(CallRef* curr) {
void FunctionValidator::visitRefI31(RefI31* curr) {
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.i31 requires gc [--enable-gc]");
if (curr->type.isRef() && curr->type.getHeapType().isShared()) {
shouldBeSubType(curr->value->type,
Type::i32,
curr->value,
"ref.i31's argument should be i32");

if (curr->type == Type::unreachable) {
return;
}

if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"ref.i31 should have a non-nullable reference type")) {
return;
}
auto heapType = curr->type.getHeapType();
if (!shouldBeTrue(heapType.isBasic() &&
heapType.getBasic(Unshared) == HeapType::i31,
curr,
"ref.i31 should have an i31 reference type")) {
return;
}
if (heapType.isShared()) {
shouldBeTrue(
getModule()->features.hasSharedEverything(),
curr,
"ref.i31_shared requires shared-everything [--enable-shared-everything]");
}
shouldBeSubType(curr->value->type,
Type::i32,
curr->value,
"ref.i31's argument should be i32");
}

void FunctionValidator::visitI31Get(I31Get* curr) {
Expand Down Expand Up @@ -2967,6 +2988,11 @@ void FunctionValidator::visitStructNew(StructNew* curr) {
if (curr->type == Type::unreachable) {
return;
}
if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"struct.new should have a non-nullable reference type")) {
return;
}
auto heapType = curr->type.getHeapType();
if (!shouldBeTrue(
heapType.isStruct(), curr, "struct.new heap type must be struct")) {
Expand Down Expand Up @@ -3200,6 +3226,11 @@ void FunctionValidator::visitArrayNew(ArrayNew* curr) {
if (curr->type == Type::unreachable) {
return;
}
if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"array.new should have a non-nullable reference type")) {
return;
}
auto heapType = curr->type.getHeapType();
if (!shouldBeTrue(
heapType.isArray(), curr, "array.new heap type must be array")) {
Expand Down Expand Up @@ -3630,12 +3661,20 @@ void FunctionValidator::visitContNew(ContNew* curr) {
curr,
"cont.new requires stack-switching [--enable-stack-switching]");

shouldBeTrue(
(curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature()) ||
curr->type == Type::unreachable,
curr,
"cont.new must be annotated with a continuation type");
if (curr->type == Type::unreachable) {
return;
}

if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"cont.new should have a non-nullable reference type")) {
return;
}

shouldBeTrue(curr->type.isContinuation() &&
curr->type.getHeapType().getContinuation().type.isSignature(),
curr,
"cont.new must be annotated with a continuation type");
}

void FunctionValidator::visitContBind(ContBind* curr) {
Expand All @@ -3657,6 +3696,16 @@ void FunctionValidator::visitContBind(ContBind* curr) {
curr->type == Type::unreachable,
curr,
"the second type annotation on cont.bind must be a continuation type");

if (curr->type == Type::unreachable) {
return;
}

if (!shouldBeTrue(curr->type.isNonNullable(),
curr,
"cont.bind should have a non-nullable reference type")) {
return;
}
}

void FunctionValidator::visitSuspend(Suspend* curr) {
Expand Down

0 comments on commit bc47696

Please sign in to comment.