50d474a268
Originally, the Torque-generated verifier for a field with type Undefined|Zero|NonNullForeign would check `f.IsUndefined() || f.IsZero() || f.IsNonNullForeign()`. At some point, we changed Torque so that it now generates the much weaker `f.IsOddball() || f.IsSmi() || f.IsForeign()`. This change returns the verifiers to their initial precision. Mostly we can use the names of abstract types to build up the correct type check expression, but a few abstract types like PodArrayOfWasmValueType have no way that we can tell them apart from their parent type at runtime. It would be confusing to have a function Object::IsPodArrayOfWasmValueType which actually just checks whether the object is a ByteArray, so this change introduces a new annotation which allows abstract type declarations to state that they should use their parent type during verification. This change also adds new test cases to help avoid future regressions of this logic. Bug: v8:7793 Change-Id: Ie5046d742fd45e0e0f6c2ba387d909e9f2ac6df1 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2469960 Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Commit-Queue: Seth Brenith <seth.brenith@microsoft.com> Cr-Commit-Position: refs/heads/master@{#70698}
188 lines
7.3 KiB
C++
188 lines
7.3 KiB
C++
// Copyright 2020 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// These tests check that Torque-generated verifier functions crash the process
|
|
// when encountering data that doesn't fit the Torque type definitions.
|
|
|
|
#include "src/api/api-inl.h"
|
|
#include "src/objects/descriptor-array.h"
|
|
#include "src/objects/map-inl.h"
|
|
#include "test/cctest/cctest.h"
|
|
#include "torque-generated/class-verifiers.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
|
|
// Defines a pair of tests with similar code. The goal is to test that a
|
|
// specific action causes a failure, but that everything else in the test case
|
|
// succeeds. The general pattern should be:
|
|
//
|
|
// TEST_PAIR(Something) {
|
|
// do_setup_steps_that_always_succeed();
|
|
// if (should_fail) {
|
|
// do_the_step_that_fails();
|
|
// }
|
|
// do_teardown_steps_that_always_succeed();
|
|
// }
|
|
//
|
|
// A corresponding entry in cctest.status specifies that all Fail* tests in this
|
|
// file must fail.
|
|
#define TEST_PAIR(Name) \
|
|
static void Name(bool should_fail); \
|
|
TEST(Pass##Name) { Name(false); } \
|
|
TEST(Fail##Name) { Name(true); } \
|
|
static void Name(bool should_fail)
|
|
|
|
#ifdef VERIFY_HEAP
|
|
|
|
TEST_PAIR(TestWrongTypeInNormalField) {
|
|
CcTest::InitializeVM();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Value> v = CompileRun("({a: 3, b: 4})");
|
|
Handle<JSObject> o = Handle<JSObject>::cast(v8::Utils::OpenHandle(*v));
|
|
Handle<Object> original_elements(
|
|
TaggedField<Object>::load(*o, JSObject::kElementsOffset), i_isolate);
|
|
CHECK(original_elements->IsFixedArrayBase());
|
|
|
|
// There must be no GC (and therefore no verifiers running) until we can
|
|
// restore the modified data.
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
// Elements must be FixedArrayBase according to the Torque definition, so a
|
|
// JSObject should cause a failure.
|
|
TaggedField<Object>::store(*o, JSObject::kElementsOffset, *o);
|
|
if (should_fail) {
|
|
TorqueGeneratedClassVerifiers::JSObjectVerify(*o, i_isolate);
|
|
}
|
|
|
|
// Put back the original value in case verifiers run on test shutdown.
|
|
TaggedField<Object>::store(*o, JSObject::kElementsOffset, *original_elements);
|
|
}
|
|
|
|
TEST_PAIR(TestWrongStrongTypeInIndexedStructField) {
|
|
CcTest::InitializeVM();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Value> v = CompileRun("({a: 3, b: 4})");
|
|
Handle<Object> o = v8::Utils::OpenHandle(*v);
|
|
Handle<Map> map(Handle<HeapObject>::cast(o)->map(), i_isolate);
|
|
Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
|
|
i_isolate);
|
|
int offset = DescriptorArray::OffsetOfDescriptorAt(1) +
|
|
DescriptorArray::kEntryKeyOffset;
|
|
Handle<Object> original_key(TaggedField<Object>::load(*descriptors, offset),
|
|
i_isolate);
|
|
CHECK(original_key->IsString());
|
|
|
|
// There must be no GC (and therefore no verifiers running) until we can
|
|
// restore the modified data.
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
// Key must be Name|Undefined according to the Torque definition, so a
|
|
// JSObject should cause a failure.
|
|
TaggedField<Object>::store(*descriptors, offset, *o);
|
|
if (should_fail) {
|
|
TorqueGeneratedClassVerifiers::DescriptorArrayVerify(*descriptors,
|
|
i_isolate);
|
|
}
|
|
|
|
// Put back the original value in case verifiers run on test shutdown.
|
|
TaggedField<Object>::store(*descriptors, offset, *original_key);
|
|
}
|
|
|
|
TEST_PAIR(TestWrongWeakTypeInIndexedStructField) {
|
|
CcTest::InitializeVM();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Value> v = CompileRun("({a: 3, b: 4})");
|
|
Handle<Object> o = v8::Utils::OpenHandle(*v);
|
|
Handle<Map> map(Handle<HeapObject>::cast(o)->map(), i_isolate);
|
|
Handle<DescriptorArray> descriptors(map->instance_descriptors(kRelaxedLoad),
|
|
i_isolate);
|
|
int offset = DescriptorArray::OffsetOfDescriptorAt(0) +
|
|
DescriptorArray::kEntryValueOffset;
|
|
Handle<Object> original_value(TaggedField<Object>::load(*descriptors, offset),
|
|
i_isolate);
|
|
|
|
// There must be no GC (and therefore no verifiers running) until we can
|
|
// restore the modified data.
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
// Value can be JSAny, which includes JSObject, and it can be Weak<Map>, but
|
|
// it can't be Weak<JSObject>.
|
|
TaggedField<Object>::store(*descriptors, offset, *o);
|
|
TorqueGeneratedClassVerifiers::DescriptorArrayVerify(*descriptors, i_isolate);
|
|
MaybeObject weak = MaybeObject::MakeWeak(MaybeObject::FromObject(*o));
|
|
TaggedField<MaybeObject>::store(*descriptors, offset, weak);
|
|
if (should_fail) {
|
|
TorqueGeneratedClassVerifiers::DescriptorArrayVerify(*descriptors,
|
|
i_isolate);
|
|
}
|
|
|
|
// Put back the original value in case verifiers run on test shutdown.
|
|
TaggedField<Object>::store(*descriptors, offset, *original_value);
|
|
}
|
|
|
|
TEST_PAIR(TestWrongOddball) {
|
|
CcTest::InitializeVM();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Value> v = CompileRun("new Date()");
|
|
Handle<JSDate> date = Handle<JSDate>::cast(v8::Utils::OpenHandle(*v));
|
|
Handle<Object> original_hour(
|
|
TaggedField<Object>::load(*date, JSDate::kHourOffset), i_isolate);
|
|
|
|
// There must be no GC (and therefore no verifiers running) until we can
|
|
// restore the modified data.
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
// Hour is Undefined|Smi|NaN. Other oddballs like null should cause a failure.
|
|
TaggedField<Object>::store(*date, JSDate::kHourOffset,
|
|
*i_isolate->factory()->null_value());
|
|
if (should_fail) {
|
|
TorqueGeneratedClassVerifiers::JSDateVerify(*date, i_isolate);
|
|
}
|
|
|
|
// Put back the original value in case verifiers run on test shutdown.
|
|
TaggedField<Object>::store(*date, JSDate::kHourOffset, *original_hour);
|
|
}
|
|
|
|
TEST_PAIR(TestWrongNumber) {
|
|
CcTest::InitializeVM();
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
v8::Local<v8::Value> v = CompileRun("new Date()");
|
|
Handle<JSDate> date = Handle<JSDate>::cast(v8::Utils::OpenHandle(*v));
|
|
Handle<Object> original_hour(
|
|
TaggedField<Object>::load(*date, JSDate::kHourOffset), i_isolate);
|
|
v8::Local<v8::Value> v2 = CompileRun("1.1");
|
|
Handle<Object> float_val = v8::Utils::OpenHandle(*v2);
|
|
|
|
// There must be no GC (and therefore no verifiers running) until we can
|
|
// restore the modified data.
|
|
DisallowHeapAllocation no_gc;
|
|
|
|
// Hour is Undefined|Smi|NaN. Other doubles like 1.1 should cause a failure.
|
|
TaggedField<Object>::store(*date, JSDate::kHourOffset, *float_val);
|
|
if (should_fail) {
|
|
TorqueGeneratedClassVerifiers::JSDateVerify(*date, i_isolate);
|
|
}
|
|
|
|
// Put back the original value in case verifiers run on test shutdown.
|
|
TaggedField<Object>::store(*date, JSDate::kHourOffset, *original_hour);
|
|
}
|
|
|
|
#endif // VERIFY_HEAP
|
|
|
|
#undef TEST_PAIR
|
|
|
|
} // namespace internal
|
|
} // namespace v8
|