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();
