chore: cherry-pick 2 changes from 3-M129 (#44232)

* chore: [31-x-y] cherry-pick 2 changes from 3-M129

* 9542895cdd3d from v8
* 81155a8f3b20 from v8

* chore: update patches
This commit is contained in:
Keeley Hammond 2024-10-14 23:10:02 -04:00 committed by GitHub
parent 48b962898d
commit 1ab051bb69
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 429 additions and 0 deletions

View file

@ -3,3 +3,5 @@ deps_add_v8_object_setinternalfieldfornodecore.patch
revert_heap_add_checks_position_info.patch
revert_api_cleanup_remove_setaccessor_and_setnativedataproperty.patch
spill_all_loop_inputs_before_entering_loop.patch
cherry-pick-9542895cdd3d.patch
cherry-pick-81155a8f3b20.patch

View file

@ -0,0 +1,282 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Victor Gomes <victorgomes@chromium.org>
Date: Wed, 2 Oct 2024 10:59:42 +0200
Subject: Consider WasmStruct in InferHasInPrototypeChain
Drive-by: add some CHECKs in not _clearly_ safe uses of AsJSObject
to turn possible vulnerablities into crashes.
Fixed: 367818758
Change-Id: Ib0464658152ce87141fa137dc6562f17b84bb6be
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5901846
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Auto-Submit: Victor Gomes <victorgomes@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#96386}
diff --git a/src/compiler/access-info.cc b/src/compiler/access-info.cc
index a81a8e80a9b6b838037c6dcc5e9b6845e9a35f7e..beefe70ecc2e2b6e2c60757d441c67e5c137159b 100644
--- a/src/compiler/access-info.cc
+++ b/src/compiler/access-info.cc
@@ -926,6 +926,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
return PropertyAccessInfo::NotFound(zone(), receiver_map, holder);
}
+ CHECK(prototype.IsJSObject());
holder = prototype.AsJSObject();
map = map_prototype_map;
diff --git a/src/compiler/heap-refs.cc b/src/compiler/heap-refs.cc
index 205d13587589724d3525928be7172f2e157811a6..bb3b5f10b037ac8cb25fc6305adcaed317c9f86d 100644
--- a/src/compiler/heap-refs.cc
+++ b/src/compiler/heap-refs.cc
@@ -1655,6 +1655,7 @@ HolderLookupResult FunctionTemplateInfoRef::LookupHolderOfExpectedType(
if (!expected_receiver_type->IsTemplateFor(prototype.object()->map())) {
return not_found;
}
+ CHECK(prototype.IsJSObject());
return HolderLookupResult(CallOptimization::kHolderFound,
prototype.AsJSObject());
}
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index b5de19c7e945badb8d5c1d5e07117b43f5b12870..45ec96a66f9826a773beb250ae924af9b98a3897 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -880,7 +880,9 @@ JSNativeContextSpecialization::InferHasInPrototypeChain(
// might be a different object each time, so it's much simpler to include
// {prototype}. That does, however, mean that we must check {prototype}'s
// map stability.
- if (!prototype.map(broker()).is_stable()) return kMayBeInPrototypeChain;
+ if (!prototype.IsJSObject() || !prototype.map(broker()).is_stable()) {
+ return kMayBeInPrototypeChain;
+ }
last_prototype = prototype.AsJSObject();
}
WhereToStart start = result == NodeProperties::kUnreliableMaps
diff --git a/test/mjsunit/wasm/regress-367818758.js b/test/mjsunit/wasm/regress-367818758.js
new file mode 100644
index 0000000000000000000000000000000000000000..69e8290c88d85699f9845ed5dddb767f1be40441
--- /dev/null
+++ b/test/mjsunit/wasm/regress-367818758.js
@@ -0,0 +1,221 @@
+// Copyright 2024 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.
+//
+// Flags: --allow-natives-syntax
+
+var kWasmH0 = 0;
+var kWasmH1 = 0x61;
+var kWasmH2 = 0x73;
+var kWasmH3 = 0x6d;
+var kWasmV0 = 0x1;
+var kWasmV1 = 0;
+var kWasmV2 = 0;
+var kWasmV3 = 0;
+let kTypeSectionCode = 1; // Function signature declarations
+let kFunctionSectionCode = 3; // Function declarations
+let kExportSectionCode = 7; // Exports
+let kCodeSectionCode = 10; // Function code
+let kWasmFunctionTypeForm = 0x60;
+let kWasmStructTypeForm = 0x5f;
+let kNoSuperType = 0xFFFFFFFF;
+let kWasmI32 = 0x7f;
+let kWasmExternRef = -0x11;
+let kLeb128Mask = 0x7f;
+let kExternalFunction = 0;
+function makeSig(params, results) {
+ return {params: params, results: results};
+}
+const kWasmOpcodes = {
+ 'End': 0x0b,
+ 'I32Const': 0x41,
+};
+function defineWasmOpcode(name, value) {
+ Object.defineProperty(globalThis, name, {value: value});
+}
+for (let name in kWasmOpcodes) {
+ defineWasmOpcode(`kExpr${name}`, kWasmOpcodes[name]);
+}
+const kPrefixOpcodes = {
+ 'GC': 0xfb,
+};
+for (let prefix in kPrefixOpcodes) {
+ defineWasmOpcode(`k${prefix}Prefix`, kPrefixOpcodes[prefix]);
+}
+let kExprStructNew = 0x00;
+let kExprExternConvertAny = 0x1b;
+class Binary {
+ constructor() {
+ this.length = 0;
+ this.buffer = new Uint8Array(8192);
+ }
+ trunc_buffer() {
+ return new Uint8Array(this.buffer.buffer, 0, this.length);
+ }
+ emit_u8(val) {
+ this.buffer[this.length++] = val;
+ }
+ emit_leb_u(val) {
+ let v = val & 0xff;
+ this.buffer[this.length++] = v;
+ }
+ emit_u32v(val) {
+ this.emit_leb_u(val);
+ }
+ emit_bytes(data) {
+ this.buffer.set(data, this.length);
+ this.length += data.length;
+ }
+ emit_string(string) {
+ let string_utf8 = string;
+ this.emit_u32v(string_utf8.length);
+ for (let i = 0; i < string_utf8.length; i++) {
+ this.emit_u8(string_utf8.charCodeAt(i));
+ }
+ }
+ emit_type(type) {
+ this.emit_u8(type >= 0 ? type : type & kLeb128Mask);
+ }
+ emit_header() {
+ this.emit_bytes([
+ kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3
+ ]);
+ }
+ emit_section(section_code, content_generator) {
+ this.emit_u8(section_code);
+ const section = new Binary;
+ content_generator(section);
+ this.emit_u32v(section.length);
+ this.emit_bytes(section.trunc_buffer());
+ }
+}
+class WasmFunctionBuilder {
+ constructor(module, name, type_index, arg_names) {
+ this.module = module;
+ this.name = name;
+ this.type_index = type_index;
+ }
+ exportAs(name) {
+ this.module.addExport(name, this.index);
+ }
+ exportFunc() {
+ this.exportAs(this.name);
+ return this;
+ }
+ addBody(body) {
+ this.body = body.concat([kExprEnd]);
+ }
+}
+function makeField(type, mutability) {
+ return {type: type, mutability: mutability};
+}
+class WasmStruct {
+ constructor(fields) {
+ this.fields = fields;
+ }
+}
+class WasmModuleBuilder {
+ constructor() {
+ this.types = [];
+ this.exports = [];
+ this.functions = [];
+ }
+ addType(type, supertype_idx = kNoSuperType, is_final = true,
+ is_shared = false) {
+ var type_copy = {params: type.params, results: type.results,
+ is_final: is_final, is_shared: is_shared,
+ supertype: supertype_idx};
+ this.types.push(type_copy);
+ return this.types.length - 1;
+ }
+ addStruct(fields = kNoSuperType = false, is_shared = false) {
+ this.types.push(new WasmStruct(fields));
+ }
+ addFunction(name, type, arg_names) {
+ let type_index =typeof type == 'number' ? type : this.addType(type);
+ let func = new WasmFunctionBuilder(this, name, type_index);
+ this.functions.push(func);
+ return func;
+ }
+ addExport(name, index) {
+ this.exports.push({name: name, kind: kExternalFunction, index: index});
+ }
+ toBuffer() {
+ let binary = new Binary;
+ let wasm = this;
+ binary.emit_header();
+ binary.emit_section(kTypeSectionCode, section => {
+ let length_with_groups = wasm.types.length;
+ section.emit_u32v(length_with_groups);
+ for (let i = 0; i < wasm.types.length; i++) {
+ let type = wasm.types[i];
+ if (type instanceof WasmStruct) {
+ section.emit_u8(kWasmStructTypeForm);
+ section.emit_u32v(type.fields.length);
+ for (let field of type.fields) {
+ section.emit_type(field.type);
+ section.emit_u8();
+ }
+ } else {
+ section.emit_u8(kWasmFunctionTypeForm);
+ section.emit_u32v();
+ section.emit_u32v(type.results.length);
+ for (let result of type.results) {
+ section.emit_type(result);
+ }
+ }
+ }
+ });
+ binary.emit_section(kFunctionSectionCode, section => {
+ section.emit_u32v(wasm.functions.length);
+ for (let func of wasm.functions) {
+ section.emit_u32v(func.type_index);
+ }
+ });
+ var exports_count = wasm.exports.length;
+ binary.emit_section(kExportSectionCode, section => {
+ section.emit_u32v(exports_count);
+ for (let exp of wasm.exports) {
+ section.emit_string(exp.name);
+ section.emit_u8();
+ section.emit_u32v();
+ }
+ });
+ binary.emit_section(kCodeSectionCode, section => {
+ section.emit_u32v(wasm.functions.length);
+ for (let func of wasm.functions) {
+ section.emit_u32v(func.body.length + 1);
+ section.emit_u8(); // 0 locals.
+ section.emit_bytes(func.body);
+ }
+ });
+ return binary.trunc_buffer();
+ }
+ instantiate() {
+ let module = this.toModule();
+ let instance = new WebAssembly.Instance(module);
+ return instance;
+ }
+ toModule() {
+ return new WebAssembly.Module(this.toBuffer());
+ }
+}
+let builder = new WasmModuleBuilder();
+let struct_type = builder.addStruct([makeField(kWasmI32)]);
+builder.addFunction('MakeStruct', makeSig([], [kWasmExternRef])).exportFunc()
+ .addBody([kExprI32Const, 42, kGCPrefix, kExprStructNew, struct_type,
+ kGCPrefix, kExprExternConvertAny]);
+let instance = builder.instantiate();
+let evil_wasm_object = instance.exports.MakeStruct();
+function evil_ctor(){
+}
+function evil_cast_jit(evil_o){
+ global_collect_node_info = evil_o; // get nodeinfo from PropertyCellStore
+ return evil_o instanceof evil_ctor;
+}
+evil_ctor.prototype = evil_wasm_object;
+%PrepareFunctionForOptimization(evil_cast_jit);
+evil_cast_jit(new evil_ctor());
+evil_cast_jit(new evil_ctor());
+%OptimizeFunctionOnNextCall(evil_cast_jit);
+evil_cast_jit();

View file

@ -0,0 +1,145 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jakob Kummerow <jkummerow@chromium.org>
Date: Tue, 24 Sep 2024 17:34:49 +0200
Subject: Properly check max module size
and allow d8-based tests for it.
Fixed: 368241697
Change-Id: Iddc9f7e669de7a1d79dccbc99bcc5fb43dad67a1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5886728
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Matthias Liedtke <mliedtke@chromium.org>
Auto-Submit: Jakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#96272}
diff --git a/src/wasm/streaming-decoder.cc b/src/wasm/streaming-decoder.cc
index 786c5aa250f055a0f69ca28403bfa679638e6465..9eb2d2fb9f1e973b04c8cf7829cc7b2849b632f9 100644
--- a/src/wasm/streaming-decoder.cc
+++ b/src/wasm/streaming-decoder.cc
@@ -294,6 +294,10 @@ void AsyncStreamingDecoder::Finish(bool can_use_compiled_module) {
if (!full_wire_bytes_.back().empty()) {
size_t total_length = 0;
for (auto& bytes : full_wire_bytes_) total_length += bytes.size();
+ if (ok()) {
+ // {DecodeSectionLength} enforces this with graceful error reporting.
+ CHECK_LE(total_length, max_module_size());
+ }
auto all_bytes = base::OwnedVector<uint8_t>::NewForOverwrite(total_length);
uint8_t* ptr = all_bytes.begin();
for (auto& bytes : full_wire_bytes_) {
@@ -627,6 +631,18 @@ std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeSectionLength::NextWithValue(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeSectionLength(%zu)\n", value_);
+ // Check if this section fits into the overall module length limit.
+ // Note: {this->module_offset_} is the position of the section ID byte,
+ // {streaming->module_offset_} is the start of the section's payload (i.e.
+ // right after the just-decoded section length varint).
+ // The latter can already exceed the max module size, when the previous
+ // section barely fit into it, and this new section's ID or length crossed
+ // the threshold.
+ uint32_t payload_start = streaming->module_offset();
+ size_t max_size = max_module_size();
+ if (payload_start > max_size || max_size - payload_start < value_) {
+ return streaming->ToErrorState();
+ }
SectionBuffer* buf =
streaming->CreateNewBuffer(module_offset_, section_id_, value_,
buffer().SubVector(0, bytes_consumed_));
diff --git a/src/wasm/wasm-engine.cc b/src/wasm/wasm-engine.cc
index 6b668b111c9d801e13f4d74f227c02029c4e80ec..8a3a855924fee296fd85e8b98d976e0c9f4c74c9 100644
--- a/src/wasm/wasm-engine.cc
+++ b/src/wasm/wasm-engine.cc
@@ -2009,10 +2009,11 @@ uint32_t max_table_init_entries() {
// {max_module_size} is declared in wasm-limits.h.
size_t max_module_size() {
- // Clamp the value of --wasm-max-module-size between 16 and just below 2GB.
+ // Clamp the value of --wasm-max-module-size between 16 and the maximum
+ // that the implementation supports.
constexpr size_t kMin = 16;
- constexpr size_t kMax = RoundDown<kSystemPointerSize>(size_t{kMaxInt});
- static_assert(kMin <= kV8MaxWasmModuleSize && kV8MaxWasmModuleSize <= kMax);
+ constexpr size_t kMax = kV8MaxWasmModuleSize;
+ static_assert(kMin <= kV8MaxWasmModuleSize);
return std::clamp(v8_flags.wasm_max_module_size.value(), kMin, kMax);
}
diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc
index d8cff07657d4dd0e71a4c677b27e96fd67cd00a3..ae04f27efb30f2bf086bd4fe4bf9a3594c38c581 100644
--- a/src/wasm/wasm-js.cc
+++ b/src/wasm/wasm-js.cc
@@ -195,8 +195,8 @@ GET_FIRST_ARGUMENT_AS(Tag)
#undef GET_FIRST_ARGUMENT_AS
i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
- const v8::FunctionCallbackInfo<v8::Value>& info, ErrorThrower* thrower,
- bool* is_shared) {
+ const v8::FunctionCallbackInfo<v8::Value>& info, size_t max_length,
+ ErrorThrower* thrower, bool* is_shared) {
DCHECK(i::ValidateCallbackInfo(info));
const uint8_t* start = nullptr;
size_t length = 0;
@@ -227,7 +227,6 @@ i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
if (length == 0) {
thrower->CompileError("BufferSource argument is empty");
}
- size_t max_length = i::wasm::max_module_size();
if (length > max_length) {
// The spec requires a CompileError for implementation-defined limits, see
// https://webassembly.github.io/spec/js-api/index.html#limits.
@@ -624,7 +623,8 @@ void WebAssemblyCompileImpl(const v8::FunctionCallbackInfo<v8::Value>& info) {
new AsyncCompilationResolver(isolate, context, promise_resolver));
bool is_shared = false;
- auto bytes = GetFirstArgumentAsBytes(info, &thrower, &is_shared);
+ auto bytes = GetFirstArgumentAsBytes(info, i::wasm::max_module_size(),
+ &thrower, &is_shared);
if (thrower.error()) {
resolver->OnCompilationFailed(thrower.Reify());
return;
@@ -656,8 +656,11 @@ void WasmStreamingCallbackForTesting(
v8::WasmStreaming::Unpack(info.GetIsolate(), info.Data());
bool is_shared = false;
+ // We don't check the buffer length up front, to allow d8 to test that the
+ // streaming decoder implementation handles overly large inputs correctly.
+ size_t unlimited = std::numeric_limits<size_t>::max();
i::wasm::ModuleWireBytes bytes =
- GetFirstArgumentAsBytes(info, &thrower, &is_shared);
+ GetFirstArgumentAsBytes(info, unlimited, &thrower, &is_shared);
if (thrower.error()) {
streaming->Abort(Utils::ToLocal(thrower.Reify()));
return;
@@ -759,7 +762,8 @@ void WebAssemblyValidateImpl(const v8::FunctionCallbackInfo<v8::Value>& info) {
ErrorThrower thrower(i_isolate, "WebAssembly.validate()");
bool is_shared = false;
- auto bytes = GetFirstArgumentAsBytes(info, &thrower, &is_shared);
+ auto bytes = GetFirstArgumentAsBytes(info, i::wasm::max_module_size(),
+ &thrower, &is_shared);
v8::ReturnValue<v8::Value> return_value = info.GetReturnValue();
@@ -838,7 +842,8 @@ void WebAssemblyModuleImpl(const v8::FunctionCallbackInfo<v8::Value>& info) {
}
bool is_shared = false;
- auto bytes = GetFirstArgumentAsBytes(info, &thrower, &is_shared);
+ auto bytes = GetFirstArgumentAsBytes(info, i::wasm::max_module_size(),
+ &thrower, &is_shared);
if (thrower.error()) {
return;
@@ -1156,7 +1161,8 @@ void WebAssemblyInstantiateImpl(
}
bool is_shared = false;
- auto bytes = GetFirstArgumentAsBytes(info, &thrower, &is_shared);
+ auto bytes = GetFirstArgumentAsBytes(info, i::wasm::max_module_size(),
+ &thrower, &is_shared);
if (thrower.error()) {
resolver->OnInstantiationFailed(thrower.Reify());
return;