SnapshotCreator: start from existing snapshot if we have one
This requires serialized data to track the number of API-provided external references separately. And it flushes out a case of serialized data corruption (stored "length" field too large) that we didn't handle without crashing. BUG=v8:6055 Review-Url: https://codereview.chromium.org/2736923002 Cr-Commit-Position: refs/heads/master@{#43649}
This commit is contained in:
parent
5a36af3ceb
commit
c478a2298d
@ -511,8 +511,11 @@ SnapshotCreator::SnapshotCreator(intptr_t* external_references,
|
||||
internal_isolate->set_array_buffer_allocator(&data->allocator_);
|
||||
internal_isolate->set_api_external_references(external_references);
|
||||
isolate->Enter();
|
||||
if (existing_snapshot) {
|
||||
internal_isolate->set_snapshot_blob(existing_snapshot);
|
||||
const StartupData* blob = existing_snapshot
|
||||
? existing_snapshot
|
||||
: i::Snapshot::DefaultSnapshotBlob();
|
||||
if (blob && blob->raw_size > 0) {
|
||||
internal_isolate->set_snapshot_blob(blob);
|
||||
i::Snapshot::Initialize(internal_isolate);
|
||||
} else {
|
||||
internal_isolate->Init(nullptr);
|
||||
|
@ -46,6 +46,7 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
|
||||
AddAccessors(isolate);
|
||||
AddStubCache(isolate);
|
||||
AddDeoptEntries(isolate);
|
||||
// API references must be added last.
|
||||
AddApiReferences(isolate);
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ class ExternalReferenceTable {
|
||||
Address address(uint32_t i) { return refs_[i].address; }
|
||||
const char* name(uint32_t i) { return refs_[i].name; }
|
||||
bool is_api_reference(uint32_t i) { return i >= api_refs_start_; }
|
||||
uint32_t num_api_references() { return size() - api_refs_start_; }
|
||||
|
||||
#ifdef DEBUG
|
||||
void increment_count(uint32_t i) { refs_[i].count++; }
|
||||
|
@ -387,6 +387,9 @@ SerializedCodeData::SerializedCodeData(const List<byte>* payload,
|
||||
SetHeaderValue(kNumCodeStubKeysOffset, num_stub_keys);
|
||||
SetHeaderValue(kPayloadLengthOffset, payload->length());
|
||||
|
||||
// Zero out any padding in the header.
|
||||
memset(data_ + kUnalignedHeaderSize, 0, kHeaderSize - kUnalignedHeaderSize);
|
||||
|
||||
// Copy reservation chunk sizes.
|
||||
CopyBytes(data_ + kHeaderSize, reinterpret_cast<byte*>(reservations.begin()),
|
||||
reservation_size);
|
||||
@ -395,6 +398,7 @@ SerializedCodeData::SerializedCodeData(const List<byte>* payload,
|
||||
CopyBytes(data_ + kHeaderSize + reservation_size,
|
||||
reinterpret_cast<byte*>(stub_keys->begin()), stub_keys_size);
|
||||
|
||||
// Zero out any padding before the payload.
|
||||
memset(data_ + payload_offset, 0, padded_payload_offset - payload_offset);
|
||||
|
||||
// Copy serialized data.
|
||||
@ -411,10 +415,14 @@ SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
|
||||
if (this->size_ < kHeaderSize) return INVALID_HEADER;
|
||||
uint32_t magic_number = GetMagicNumber();
|
||||
if (magic_number != ComputeMagicNumber(isolate)) return MAGIC_NUMBER_MISMATCH;
|
||||
if (GetExtraReferences() > GetExtraReferences(isolate)) {
|
||||
return MAGIC_NUMBER_MISMATCH;
|
||||
}
|
||||
uint32_t version_hash = GetHeaderValue(kVersionHashOffset);
|
||||
uint32_t source_hash = GetHeaderValue(kSourceHashOffset);
|
||||
uint32_t cpu_features = GetHeaderValue(kCpuFeaturesOffset);
|
||||
uint32_t flags_hash = GetHeaderValue(kFlagHashOffset);
|
||||
uint32_t payload_length = GetHeaderValue(kPayloadLengthOffset);
|
||||
uint32_t c1 = GetHeaderValue(kChecksum1Offset);
|
||||
uint32_t c2 = GetHeaderValue(kChecksum2Offset);
|
||||
if (version_hash != Version::Hash()) return VERSION_MISMATCH;
|
||||
@ -423,6 +431,12 @@ SerializedCodeData::SanityCheckResult SerializedCodeData::SanityCheck(
|
||||
return CPU_FEATURES_MISMATCH;
|
||||
}
|
||||
if (flags_hash != FlagList::Hash()) return FLAGS_MISMATCH;
|
||||
uint32_t max_payload_length =
|
||||
this->size_ -
|
||||
POINTER_SIZE_ALIGN(kHeaderSize +
|
||||
GetHeaderValue(kNumReservationsOffset) * kInt32Size +
|
||||
GetHeaderValue(kNumCodeStubKeysOffset) * kInt32Size);
|
||||
if (payload_length > max_payload_length) return LENGTH_MISMATCH;
|
||||
if (!Checksum(DataWithoutHeader()).Check(c1, c2)) return CHECKSUM_MISMATCH;
|
||||
return CHECK_SUCCESS;
|
||||
}
|
||||
|
@ -85,24 +85,25 @@ class SerializedCodeData : public SerializedData {
|
||||
CPU_FEATURES_MISMATCH = 4,
|
||||
FLAGS_MISMATCH = 5,
|
||||
CHECKSUM_MISMATCH = 6,
|
||||
INVALID_HEADER = 7
|
||||
INVALID_HEADER = 7,
|
||||
LENGTH_MISMATCH = 8
|
||||
};
|
||||
|
||||
// The data header consists of uint32_t-sized entries:
|
||||
// [0] magic number and external reference count
|
||||
// [1] version hash
|
||||
// [2] source hash
|
||||
// [3] cpu features
|
||||
// [4] flag hash
|
||||
// [5] number of code stub keys
|
||||
// [6] number of reservation size entries
|
||||
// [7] payload length
|
||||
// [8] payload checksum part 1
|
||||
// [9] payload checksum part 2
|
||||
// [0] magic number and (internally provided) external reference count
|
||||
// [1] extra (API-provided) external reference count
|
||||
// [2] version hash
|
||||
// [3] source hash
|
||||
// [4] cpu features
|
||||
// [5] flag hash
|
||||
// [6] number of code stub keys
|
||||
// [7] number of reservation size entries
|
||||
// [8] payload length
|
||||
// [9] payload checksum part 1
|
||||
// [10] payload checksum part 2
|
||||
// ... reservations
|
||||
// ... code stub keys
|
||||
// ... serialized payload
|
||||
static const int kVersionHashOffset = kMagicNumberOffset + kInt32Size;
|
||||
static const int kSourceHashOffset = kVersionHashOffset + kInt32Size;
|
||||
static const int kCpuFeaturesOffset = kSourceHashOffset + kInt32Size;
|
||||
static const int kFlagHashOffset = kCpuFeaturesOffset + kInt32Size;
|
||||
@ -111,7 +112,8 @@ class SerializedCodeData : public SerializedData {
|
||||
static const int kPayloadLengthOffset = kNumCodeStubKeysOffset + kInt32Size;
|
||||
static const int kChecksum1Offset = kPayloadLengthOffset + kInt32Size;
|
||||
static const int kChecksum2Offset = kChecksum1Offset + kInt32Size;
|
||||
static const int kHeaderSize = kChecksum2Offset + kInt32Size;
|
||||
static const int kUnalignedHeaderSize = kChecksum2Offset + kInt32Size;
|
||||
static const int kHeaderSize = POINTER_SIZE_ALIGN(kUnalignedHeaderSize);
|
||||
|
||||
// Used when consuming.
|
||||
static const SerializedCodeData FromCachedData(
|
||||
|
@ -76,6 +76,10 @@ void Deserializer::Initialize(Isolate* isolate) {
|
||||
external_reference_table_ = ExternalReferenceTable::instance(isolate);
|
||||
CHECK_EQ(magic_number_,
|
||||
SerializedData::ComputeMagicNumber(external_reference_table_));
|
||||
// The current isolate must have at least as many API-provided external
|
||||
// references as the to-be-deserialized snapshot expects and refers to.
|
||||
CHECK_LE(num_extra_references_,
|
||||
SerializedData::GetExtraReferences(external_reference_table_));
|
||||
}
|
||||
|
||||
void Deserializer::Deserialize(Isolate* isolate) {
|
||||
|
@ -34,6 +34,7 @@ class Deserializer : public SerializerDeserializer {
|
||||
: isolate_(NULL),
|
||||
source_(data->Payload()),
|
||||
magic_number_(data->GetMagicNumber()),
|
||||
num_extra_references_(data->GetExtraReferences()),
|
||||
next_map_index_(0),
|
||||
external_reference_table_(NULL),
|
||||
deserialized_large_objects_(0),
|
||||
@ -125,6 +126,7 @@ class Deserializer : public SerializerDeserializer {
|
||||
|
||||
SnapshotByteSource source_;
|
||||
uint32_t magic_number_;
|
||||
uint32_t num_extra_references_;
|
||||
|
||||
// The address of the next object that will be allocated in each space.
|
||||
// Each space has a number of chunks reserved by the GC, with each chunk
|
||||
|
@ -245,14 +245,26 @@ class SerializedData {
|
||||
}
|
||||
|
||||
uint32_t GetMagicNumber() const { return GetHeaderValue(kMagicNumberOffset); }
|
||||
uint32_t GetExtraReferences() const {
|
||||
return GetHeaderValue(kExtraExternalReferencesOffset);
|
||||
}
|
||||
|
||||
class ChunkSizeBits : public BitField<uint32_t, 0, 31> {};
|
||||
class IsLastChunkBits : public BitField<bool, 31, 1> {};
|
||||
|
||||
static uint32_t ComputeMagicNumber(ExternalReferenceTable* table) {
|
||||
uint32_t external_refs = table->size();
|
||||
uint32_t external_refs = table->size() - table->num_api_references();
|
||||
return 0xC0DE0000 ^ external_refs;
|
||||
}
|
||||
static uint32_t GetExtraReferences(ExternalReferenceTable* table) {
|
||||
return table->num_api_references();
|
||||
}
|
||||
|
||||
static const int kMagicNumberOffset = 0;
|
||||
static const int kExtraExternalReferencesOffset =
|
||||
kMagicNumberOffset + kInt32Size;
|
||||
static const int kVersionHashOffset =
|
||||
kExtraExternalReferencesOffset + kInt32Size;
|
||||
|
||||
protected:
|
||||
void SetHeaderValue(int offset, uint32_t value) {
|
||||
@ -271,13 +283,15 @@ class SerializedData {
|
||||
static uint32_t ComputeMagicNumber(Isolate* isolate) {
|
||||
return ComputeMagicNumber(ExternalReferenceTable::instance(isolate));
|
||||
}
|
||||
static uint32_t GetExtraReferences(Isolate* isolate) {
|
||||
return GetExtraReferences(ExternalReferenceTable::instance(isolate));
|
||||
}
|
||||
|
||||
void SetMagicNumber(Isolate* isolate) {
|
||||
SetHeaderValue(kMagicNumberOffset, ComputeMagicNumber(isolate));
|
||||
SetHeaderValue(kExtraExternalReferencesOffset, GetExtraReferences(isolate));
|
||||
}
|
||||
|
||||
static const int kMagicNumberOffset = 0;
|
||||
|
||||
byte* data_;
|
||||
int size_;
|
||||
bool owns_data_;
|
||||
|
@ -195,7 +195,7 @@ SnapshotData::SnapshotData(const Serializer* serializer) {
|
||||
|
||||
// Set header values.
|
||||
SetMagicNumber(serializer->isolate());
|
||||
SetHeaderValue(kCheckSumOffset, Version::Hash());
|
||||
SetHeaderValue(kVersionHashOffset, Version::Hash());
|
||||
SetHeaderValue(kNumReservationsOffset, reservations.length());
|
||||
SetHeaderValue(kPayloadLengthOffset, payload->length());
|
||||
|
||||
@ -209,7 +209,7 @@ SnapshotData::SnapshotData(const Serializer* serializer) {
|
||||
}
|
||||
|
||||
bool SnapshotData::IsSane() {
|
||||
return GetHeaderValue(kCheckSumOffset) == Version::Hash();
|
||||
return GetHeaderValue(kVersionHashOffset) == Version::Hash();
|
||||
}
|
||||
|
||||
Vector<const SerializedData::Reservation> SnapshotData::Reservations() const {
|
||||
|
@ -39,14 +39,14 @@ class SnapshotData : public SerializedData {
|
||||
bool IsSane();
|
||||
|
||||
// The data header consists of uint32_t-sized entries:
|
||||
// [0] magic number and external reference count
|
||||
// [1] version hash
|
||||
// [2] number of reservation size entries
|
||||
// [3] payload length
|
||||
// [0] magic number and (internal) external reference count
|
||||
// [1] API-provided external reference count
|
||||
// [2] version hash
|
||||
// [3] number of reservation size entries
|
||||
// [4] payload length
|
||||
// ... reservations
|
||||
// ... serialized payload
|
||||
static const int kCheckSumOffset = kMagicNumberOffset + kInt32Size;
|
||||
static const int kNumReservationsOffset = kCheckSumOffset + kInt32Size;
|
||||
static const int kNumReservationsOffset = kVersionHashOffset + kInt32Size;
|
||||
static const int kPayloadLengthOffset = kNumReservationsOffset + kInt32Size;
|
||||
static const int kHeaderSize = kPayloadLengthOffset + kInt32Size;
|
||||
};
|
||||
|
@ -237,9 +237,10 @@ class WasmSerializationTest {
|
||||
}
|
||||
|
||||
void InvalidateVersion() {
|
||||
uint32_t* buffer = reinterpret_cast<uint32_t*>(
|
||||
const_cast<uint8_t*>(serialized_bytes_.first));
|
||||
buffer[SerializedCodeData::kVersionHashOffset] = Version::Hash() + 1;
|
||||
uint32_t* slot = reinterpret_cast<uint32_t*>(
|
||||
const_cast<uint8_t*>(serialized_bytes_.first) +
|
||||
SerializedCodeData::kVersionHashOffset);
|
||||
*slot = Version::Hash() + 1;
|
||||
}
|
||||
|
||||
void InvalidateWireBytes() {
|
||||
@ -247,6 +248,13 @@ class WasmSerializationTest {
|
||||
wire_bytes_.second / 2);
|
||||
}
|
||||
|
||||
void InvalidateLength() {
|
||||
uint32_t* slot = reinterpret_cast<uint32_t*>(
|
||||
const_cast<uint8_t*>(serialized_bytes_.first) +
|
||||
SerializedCodeData::kPayloadLengthOffset);
|
||||
*slot = 0xfefefefeu;
|
||||
}
|
||||
|
||||
v8::MaybeLocal<v8::WasmCompiledModule> Deserialize() {
|
||||
ErrorThrower thrower(current_isolate(), "");
|
||||
v8::MaybeLocal<v8::WasmCompiledModule> deserialized =
|
||||
@ -406,6 +414,17 @@ TEST(DeserializeNoSerializedData) {
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
TEST(DeserializeInvalidLength) {
|
||||
WasmSerializationTest test;
|
||||
{
|
||||
HandleScope scope(test.current_isolate());
|
||||
test.InvalidateLength();
|
||||
test.DeserializeAndRun();
|
||||
}
|
||||
Cleanup(test.current_isolate());
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
TEST(DeserializeWireBytesAndSerializedDataInvalid) {
|
||||
WasmSerializationTest test;
|
||||
{
|
||||
|
@ -288,8 +288,9 @@ def Main(argv):
|
||||
return_code = 0
|
||||
for c in configs:
|
||||
return_code += configs[c].Build()
|
||||
for c in configs:
|
||||
return_code += configs[c].RunTests()
|
||||
if return_code == 0:
|
||||
for c in configs:
|
||||
return_code += configs[c].RunTests()
|
||||
if return_code == 0:
|
||||
_Call("notify-send 'Done!' 'V8 compilation finished successfully.'",
|
||||
silent=True)
|
||||
|
Loading…
Reference in New Issue
Block a user