Commit ce6b373d authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Disallow type checks from eqref and anyref

As per the latest wasm-gc spec, type checks are only allowed from
subtypes of dataref and funcref. To cast from a more general type,
specialized classification instructions need to be used (will come
later).

Bug: v8:7748
Change-Id: I29de48f445d652c5fc4e266d82e7d2e32cd7b6d3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2649262
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72408}
parent 84dec706
......@@ -5821,11 +5821,6 @@ WasmGraphBuilder::Callbacks WasmGraphBuilder::BranchCallbacks(
void WasmGraphBuilder::TypeCheck(
Node* object, Node* rtt, WasmGraphBuilder::ObjectReferenceKnowledge config,
bool null_succeeds, Callbacks callbacks) {
if (config.object_can_be_i31) {
callbacks.fail_if(gasm_->IsI31(object));
} else {
AssertFalse(mcgraph(), gasm_.get(), gasm_->IsI31(object));
}
if (config.object_can_be_null) {
(null_succeeds ? callbacks.succeed_if
: callbacks.fail_if)(gasm_->WordEqual(object, RefNull()));
......
......@@ -168,7 +168,6 @@ class WasmGraphBuilder {
struct ObjectReferenceKnowledge {
bool object_can_be_null;
bool object_must_be_data_ref;
bool object_can_be_i31;
int8_t rtt_depth;
};
enum EnforceBoundsCheck : bool { // --
......
......@@ -4410,7 +4410,6 @@ class LiftoffCompiler {
LiftoffRegister rtt_reg = pinned.set(__ PopToRegister(pinned));
LiftoffRegister obj_reg = pinned.set(__ PopToRegister(pinned));
bool obj_can_be_i31 = IsSubtypeOf(kWasmI31Ref, obj.type, decoder->module_);
// Reserve all temporary registers up front, so that the cache state
// tracking doesn't get confused by the following conditional jumps.
LiftoffRegister tmp1 =
......@@ -4418,9 +4417,6 @@ class LiftoffCompiler {
? LiftoffRegister(opt_scratch)
: pinned.set(__ GetUnusedRegister(kGpReg, pinned));
LiftoffRegister tmp2 = pinned.set(__ GetUnusedRegister(kGpReg, pinned));
if (obj_can_be_i31) {
__ emit_smi_check(obj_reg.gp(), no_match, LiftoffAssembler::kJumpOnSmi);
}
if (obj.type.is_nullable()) {
LoadNullValue(tmp1.gp(), pinned);
__ emit_cond_jump(kEqual, null_succeeds ? &match : no_match, obj.type,
......
......@@ -4085,12 +4085,20 @@ class WasmFullDecoder : public WasmDecoder<validate> {
case kExprRefTest: {
// "Tests whether {obj}'s runtime type is a runtime subtype of {rtt}."
Value rtt = Pop(1);
Value obj = Pop(0, kWasmAnyRef);
Value obj = Pop(0);
Value* value = Push(kWasmI32);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
}
if (!obj.type.is_bottom() && !rtt.type.is_bottom()) {
if (!VALIDATE(IsSubtypeOf(
ValueType::Ref(rtt.type.ref_index(), kNonNullable), obj.type,
......@@ -4106,11 +4114,19 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprRefCast: {
Value rtt = Pop(1);
Value obj = Pop(0, kWasmAnyRef);
Value obj = Pop(0);
if (!VALIDATE(rtt.type.is_rtt() || rtt.type.is_bottom())) {
PopTypeError(1, rtt, "rtt");
return 0;
}
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
}
if (!obj.type.is_bottom() && !rtt.type.is_bottom()) {
if (!VALIDATE(IsSubtypeOf(
ValueType::Ref(rtt.type.ref_index(), kNonNullable), obj.type,
......@@ -4139,9 +4155,12 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return 0;
}
Value obj = Pop(0);
if (!VALIDATE(obj.type.is_object_reference_type() ||
rtt.type.is_bottom())) {
PopTypeError(0, obj, "reference");
if (!VALIDATE(IsSubtypeOf(obj.type, kWasmFuncRef, this->module_) ||
IsSubtypeOf(obj.type,
ValueType::Ref(HeapType::kData, kNullable),
this->module_) ||
obj.type.is_bottom())) {
PopTypeError(0, obj, "subtype of (ref null func) or (ref null data)");
return 0;
}
// The static type of {obj} must be a supertype of {rtt}'s type.
......
......@@ -989,7 +989,6 @@ class WasmGraphBuildingInterface {
result.object_can_be_null = object_type.is_nullable();
DCHECK(object_type.is_object_reference_type()); // Checked by validation.
result.object_must_be_data_ref = is_data_ref_type(object_type, module);
result.object_can_be_i31 = IsSubtypeOf(kWasmI31Ref, object_type, module);
result.rtt_depth = rtt_type.has_depth() ? rtt_type.depth() : -1;
return result;
}
......
......@@ -347,6 +347,7 @@ WASM_COMPILED_EXEC_TEST(WasmBrOnNull) {
WASM_COMPILED_EXEC_TEST(BrOnCast) {
WasmGCTester tester(execution_tier);
FLAG_experimental_liftoff_extern_ref = true;
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
const byte type_index = tester.DefineStruct({F(kWasmI32, true)});
const byte other_type_index = tester.DefineStruct({F(kWasmF32, true)});
const byte rtt_index =
......@@ -354,7 +355,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WasmInitExpr::RttCanon(
static_cast<HeapType::Representation>(type_index)));
const byte kTestStruct = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
{WASM_BLOCK(WASM_LOCAL_SET(0, WASM_I32V(111)),
// Pipe a struct through a local so it's statically typed
// as eqref.
......@@ -371,20 +372,8 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WASM_DROP, WASM_LOCAL_SET(0, WASM_I32V(333))),
WASM_LOCAL_GET(0), kExprEnd});
const byte kTestI31 = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
{WASM_BLOCK(WASM_LOCAL_SET(0, WASM_I32V(111)),
// Pipe an i31ref through a local so it's statically typed
// as eqref.
WASM_LOCAL_SET(1, WASM_I31_NEW(WASM_I32V(42))),
WASM_LOCAL_GET(1),
// The i31 is not a struct, so this branch isn't taken.
WASM_BR_ON_CAST(0, WASM_GLOBAL_GET(rtt_index)), WASM_DROP,
WASM_LOCAL_SET(0, WASM_I32V(222))), // Final result.
WASM_LOCAL_GET(0), kExprEnd});
const byte kTestNull = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
{WASM_BLOCK(WASM_LOCAL_SET(0, WASM_I32V(111)),
WASM_LOCAL_GET(1), // Put a nullref onto the value stack.
// Neither of these branches is taken for nullref.
......@@ -395,7 +384,7 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
WASM_LOCAL_GET(0), kExprEnd});
const byte kTypedAfterBranch = tester.DefineFunction(
tester.sigs.i_v(), {kWasmI32, kWasmEqRef},
tester.sigs.i_v(), {kWasmI32, kDataRefNull},
{WASM_LOCAL_SET(1, WASM_STRUCT_NEW_WITH_RTT(type_index, WASM_I32V(42),
WASM_GLOBAL_GET(rtt_index))),
WASM_BLOCK(WASM_LOCAL_SET(
......@@ -415,7 +404,6 @@ WASM_COMPILED_EXEC_TEST(BrOnCast) {
tester.CompileModule();
tester.CheckResult(kTestStruct, 222);
tester.CheckResult(kTestI31, 222);
tester.CheckResult(kTestNull, 333);
tester.CheckResult(kTypedAfterBranch, 42);
}
......@@ -1116,7 +1104,9 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
const byte SuperType = tester.DefineStruct({F(wasm::kWasmI32, true)});
const byte SubType =
tester.DefineStruct({F(wasm::kWasmI32, true), F(wasm::kWasmI32, true)});
const byte ListType = tester.DefineArray(wasm::kWasmEqRef, true);
ValueType kDataRefNull = ValueType::Ref(HeapType::kData, kNullable);
const byte ListType = tester.DefineArray(kDataRefNull, true);
const byte List =
tester.AddGlobal(ValueType::Ref(ListType, kNullable), true,
......
......@@ -4229,12 +4229,19 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
static_cast<HeapType::Representation>(
builder.AddStruct({F(kWasmI16, true), F(kWasmI32, false)}));
HeapType::Representation func_heap_1 =
static_cast<HeapType::Representation>(builder.AddSignature(sigs.i_i()));
HeapType::Representation func_heap_2 =
static_cast<HeapType::Representation>(builder.AddSignature(sigs.i_v()));
// Passing/failing tests due to static subtyping.
std::tuple<HeapType::Representation, HeapType::Representation, bool> tests[] =
{{HeapType::kAny, array_heap, true},
{HeapType::kAny, super_struct_heap, true},
{HeapType::kEq, array_heap, true},
{HeapType::kEq, super_struct_heap, true},
{{HeapType::kData, array_heap, true},
{HeapType::kData, super_struct_heap, true},
{HeapType::kFunc, func_heap_1, true},
{func_heap_1, func_heap_1, true},
{func_heap_1, func_heap_2, false},
{super_struct_heap, sub_struct_heap, true},
{sub_struct_heap, super_struct_heap, false},
{sub_struct_heap, array_heap, false},
......@@ -4290,24 +4297,28 @@ TEST_F(FunctionBodyDecoderTest, RefTestCast) {
sigs.v_v(),
{WASM_REF_TEST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop},
kAppendEnd,
"ref.test[0] expected type anyref, found i32.const of type i32");
"ref.test[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32");
ExpectFailure(
sigs.v_v(),
{WASM_REF_CAST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop},
kAppendEnd,
"ref.cast[0] expected type anyref, found i32.const of type i32");
"ref.cast[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32");
// Trivial type error.
ExpectFailure(
sigs.v_v(),
{WASM_REF_TEST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop},
kAppendEnd,
"ref.test[0] expected type anyref, found i32.const of type i32");
"ref.test[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32");
ExpectFailure(
sigs.v_v(),
{WASM_REF_CAST(WASM_I32V(1), WASM_RTT_CANON(array_heap)), kExprDrop},
kAppendEnd,
"ref.cast[0] expected type anyref, found i32.const of type i32");
"ref.cast[0] expected subtype of (ref null func) or (ref null data), "
"found i32.const of type i32");
}
// This tests that num_locals_ in decoder remains consistent, even if we fail
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment