High chrome UAF 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in V8
DescriptionUse after free in V8
ComponentV8
Bug ClassUAF
Tracker567088929
Fix commit6937df3c3da7 (v8/v8) +547/-23
CISA KEVNot listed
CreditedXinyang Ge (Anthropic), assisted by Claude
Disclosed2026-10-13

Changed Functions

FunctionChangeNotes
DefineNamedOwnPropertyParameters
src/compiler/js-operator.h
modified
switch
src/debug/debug-evaluate.cc
modified
if
src/interpreter/bytecode-generator.cc
modified
Base
test/mjsunit/compiler/define-const-data-field-class-field.js
modified
D
test/mjsunit/compiler/define-const-data-field-class-field.js
modified

Files Changed

  • src/maglev/maglev-graph-builder.h
  • src/compiler/js-operator.cc
  • src/interpreter/interpreter-generator.cc
  • src/compiler/js-operator.h
  • test/unittests/interpreter/bytecode_expectations/TopLevelObjectLiterals.golden
  • src/maglev/maglev-graph-builder.cc
  • test/mjsunit/maglev/define-const-data-field-class-field.js
  • src/compiler/bytecode-graph-builder.cc
  • test/unittests/interpreter/bytecode-array-builder-unittest.cc
  • src/interpreter/bytecode-array-builder.cc
  • src/interpreter/bytecode-array-builder.h
  • src/compiler/js-native-context-specialization.cc
  • test/mjsunit/compiler/define-const-data-field-class-field.js
  • src/debug/debug-evaluate.cc
  • src/interpreter/bytecode-generator.cc
  • test/mjsunit/maglev/define-const-data-field-spread-literal.js
  • test/mjsunit/maglev/define-const-data-field-object-literal.js
  • test/unittests/interpreter/bytecode_expectations/ObjectLiterals.golden
  • src/baseline/baseline-compiler.cc
  • src/interpreter/bytecodes.h
From 6937df3c3da7b499d60d51f349c2e330b7f8ee15 Mon Sep 17 00:00:00 2001
From: Marja Hölttä <[email protected]>
Date: Fri, 02 Oct 2026 15:11:18 +0200
Subject: [PATCH] [maglev, turbofan] Fix DefineNamedOwnProperty to invalidate constness correctly

DefineNamedOwnProperty was used in object literals, spread object
literals, and class field initializers.

In object literals, we're writing into a fresh object where the field is
uninitialized. That part should stay as is.

But in spread object literals and class field initializers, the define
might invalidate constness of the field, in case the field already had a
value before the DefineNamedOwnProperty.

Changes in this CL:

1) Add a new bytecode (DefineNamedOwnPropertyInLiteral) for the object
literal case (there we don't need additional checks) and make Maglev &
Turbofan deopt instead of doing DefineNamedOwnProperty with a const
field (this will invalidate constness).

2) Unify AccessModes in Maglev and Turbofan; For the old
DefineNamedOwnProperty, Turbofan was always using
AccessMode::kStoreInLiteral and Maglev was using AccessMode::kDefine.
Now both use AccessMode::kStoreInLiteral for
DefineNamedOwnPropertyInLiteral and AccessMode::kDefine for
DefineNamedOwnProperty.

Fixed: 567088929
Change-Id: I48e2d9014be77947506e6cd6294d77e5f80ef762
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8501161
Commit-Queue: Marja Hölttä <[email protected]>
Reviewed-by: Leszek Swirski <[email protected]>
Cr-Commit-Position: refs/heads/main@{#110308}
---
diff --git a/src/baseline/baseline-compiler.cc b/src/baseline/baseline-compiler.cc
index 91fa8f4..b09ed33 100644
--- a/src/baseline/baseline-compiler.cc
+++ b/src/baseline/baseline-compiler.cc
@@ -1211,6 +1211,10 @@
       FeedbackSlotAsTagged(2));         // slot
 }
 
+void BaselineCompiler::VisitDefineNamedOwnPropertyInLiteral() {
+  VisitDefineNamedOwnProperty();
+}
+
 void BaselineCompiler::VisitSetKeyedProperty() {
   // KeyedStoreIC is currently a base class for multiple keyed property store
   // operations and contains mixed logic for set and define operations,
diff --git a/src/compiler/bytecode-graph-builder.cc b/src/compiler/bytecode-graph-builder.cc
index 9a8b25b..bdc98b4 100644
--- a/src/compiler/bytecode-graph-builder.cc
+++ b/src/compiler/bytecode-graph-builder.cc
@@ -241,6 +241,8 @@
     kSet,
     // Define value to the receiver without checking the prototype chain.
     kDefineOwn,
+    // Like kDefineOwn, but inside an object literal
+    kDefineOwnInLiteral
   };
   void BuildNamedStore(NamedStoreMode store_mode);
   void BuildLdaLookupSlot(TypeofMode typeof_mode);
@@ -2194,7 +2196,12 @@
     DCHECK_EQ(FeedbackSlotKind::kDefineNamedOwn,
               broker()->GetFeedbackSlotKind(feedback));
 
-    op = javascript()->DefineNamedOwnProperty(name, feedback);
+    op = javascript()->DefineNamedOwnProperty(false, name, feedback);
+  } else if (store_mode == NamedStoreMode::kDefineOwnInLiteral) {
+    DCHECK_EQ(FeedbackSlotKind::kDefineNamedOwn,
+              broker()->GetFeedbackSlotKind(feedback));
+
+    op = javascript()->DefineNamedOwnProperty(true, name, feedback);
   } else {
     DCHECK_EQ(NamedStoreMode::kSet, store_mode);
     LanguageMode language_mode =
@@ -2316,6 +2323,10 @@
   BuildNamedStore(NamedStoreMode::kDefineOwn);
 }
 
+void BytecodeGraphBuilder::VisitDefineNamedOwnPropertyInLiteral() {
+  BuildNamedStore(NamedStoreMode::kDefineOwnInLiteral);
+}
+
 void BytecodeGraphBuilder::VisitSetKeyedProperty() {
   PrepareEagerCheckpoint();
   Node* value = environment()->LookupAccumulator();
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
index 0562b88..ae983cf 100644
--- a/src/compiler/js-native-context-specialization.cc
+++ b/src/compiler/js-native-context-specialization.cc
@@ -2317,9 +2317,9 @@
   JSDefineNamedOwnPropertyNode n(node);
   DefineNamedOwnPropertyParameters const& p = n.Parameters();
   if (!p.feedback().IsValid()) return NoChange();
-  return ReducePropertyAccess(node, nullptr, p.name(), n.value(),
-                              FeedbackSource(p.feedback()),
-                              AccessMode::kStoreInLiteral);
+  return ReducePropertyAccess(
+      node, nullptr, p.name(), n.value(), FeedbackSource(p.feedback()),
+      p.in_literal() ? AccessMode::kStoreInLiteral : AccessMode::kDefine);
 }
 
 Reduction JSNativeContextSpecialization::ReduceElementAccessOnString(
@@ -3408,7 +3408,8 @@
               AccessBuilder::ForJSObjectPropertiesOrHashKnownPointer()),
           storage, effect, control);
     }
-    if (access_info.IsFastDataConstant() && access_mode == AccessMode::kStore &&
+    if (access_info.IsFastDataConstant() &&
+        access_mode != AccessMode::kStoreInLiteral &&
         !access_info.HasTransitionMap()) {
       Node* deoptimize = graph()->NewNode(
           simplified()->CheckIf(DeoptimizeReason::kStoreToConstant),
diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc
index cecff45..adb3c84 100644
--- a/src/compiler/js-operator.cc
+++ b/src/compiler/js-operator.cc
@@ -222,7 +222,8 @@
 
 bool operator==(DefineNamedOwnPropertyParameters const& lhs,
                 DefineNamedOwnPropertyParameters const& rhs) {
-  return lhs.name_.object().location() == rhs.name_.object().location() &&
+  return lhs.in_literal() == rhs.in_literal() &&
+         lhs.name_.object().location() == rhs.name_.object().location() &&
          lhs.feedback() == rhs.feedback();
 }
 
@@ -232,7 +233,7 @@
 }
 
 size_t hash_value(DefineNamedOwnPropertyParameters const& p) {
-  return base::hash_combine(p.name_.object().location(),
+  return base::hash_combine(p.in_literal(), p.name_.object().location(),
                             FeedbackSource::Hash()(p.feedback()));
 }
 
@@ -1339,12 +1340,12 @@
 }
 
 const Operator* JSOperatorBuilder::DefineNamedOwnProperty(
-    NameRef name, FeedbackSource const& feedback) {
+    bool in_literal, NameRef name, FeedbackSource const& feedback) {
   static constexpr int kObject = 1;
   static constexpr int kValue = 1;
   static constexpr int kFeedbackVector = 1;
   static constexpr int kArity = kObject + kValue + kFeedbackVector;
-  DefineNamedOwnPropertyParameters parameters(name, feedback);
+  DefineNamedOwnPropertyParameters parameters(in_literal, name, feedback);
   return zone()->New<Operator1<DefineNamedOwnPropertyParameters>>(   // --
       IrOpcode::kJSDefineNamedOwnProperty, Operator::kNoProperties,  // opcode
       "JSDefineNamedOwnProperty",                                    // name
diff --git a/src/compiler/js-operator.h b/src/compiler/js-operator.h
index 2ec4d09b..80baa97 100644
--- a/src/compiler/js-operator.h
+++ b/src/compiler/js-operator.h
@@ -418,13 +418,16 @@
 // Defines parameters for JSDefineNamedOwnProperty operator.
 class DefineNamedOwnPropertyParameters final {
  public:
-  DefineNamedOwnPropertyParameters(NameRef name, FeedbackSource const& feedback)
-      : name_(name), feedback_(feedback) {}
+  DefineNamedOwnPropertyParameters(bool in_literal, NameRef name,
+                                   FeedbackSource const& feedback)
+      : in_literal_(in_literal), name_(name), feedback_(feedback) {}
 
   NameRef name() const { return name_; }
   FeedbackSource const& feedback() const { return feedback_; }
+  bool in_literal() const { return in_literal_; }
 
  private:
+  bool in_literal_;
   const NameRef name_;
   FeedbackSource const feedback_;
 
@@ -1157,7 +1160,7 @@
   const Operator* SetNamedProperty(LanguageMode language_mode, NameRef name,
                                    FeedbackSource const& feedback);
 
-  const Operator* DefineNamedOwnProperty(NameRef name,
+  const Operator* DefineNamedOwnProperty(bool in_literal, NameRef name,
                                          FeedbackSource const& feedback);
   const Operator* DefineKeyedOwnPropertyInLiteral(
       const FeedbackSource& feedback);
diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc
index 40feb7b..0ad5594 100644
--- a/src/debug/debug-evaluate.cc
+++ b/src/debug/debug-evaluate.cc
@@ -1343,6 +1343,7 @@
   switch (bytecode) {
     case Bytecode::kSetNamedProperty:
     case Bytecode::kDefineNamedOwnProperty:
+    case Bytecode::kDefineNamedOwnPropertyInLiteral:
     case Bytecode::kSetKeyedProperty:
     case Bytecode::kStaInArrayLiteral:
     case Bytecode::kDefineKeyedOwnPropertyInLiteral:
diff --git a/src/interpreter/bytecode-array-builder.cc b/src/interpreter/bytecode-array-builder.cc
index b531135..d50b594 100644
--- a/src/interpreter/bytecode-array-builder.cc
+++ b/src/interpreter/bytecode-array-builder.cc
@@ -1055,6 +1055,17 @@
   return *this;
 }
 
+BytecodeArrayBuilder& BytecodeArrayBuilder::DefineNamedOwnPropertyInLiteral(
+    Register object, const AstRawString* name, int feedback_slot) {
+  size_t name_index = GetConstantPoolEntry(name);
+  // Ensure that the store operation is in sync with the IC slot kind.
+  DCHECK_EQ(
+      FeedbackSlotKind::kDefineNamedOwn,
+      feedback_vector_spec()->GetKind(FeedbackVector::ToSlot(feedback_slot)));
+  OutputDefineNamedOwnPropertyInLiteral(object, name_index, feedback_slot);
+  return *this;
+}
+
 BytecodeArrayBuilder& BytecodeArrayBuilder::SetKeyedProperty(
     Register object, Register key, int feedback_slot,
     LanguageMode language_mode) {
diff --git a/src/interpreter/bytecode-array-builder.h b/src/interpreter/bytecode-array-builder.h
index 2e8a134..c43d1d0 100644
--- a/src/interpreter/bytecode-array-builder.h
+++ b/src/interpreter/bytecode-array-builder.h
@@ -209,6 +209,13 @@
                                                const AstRawString* name,
                                                int feedback_slot);
 
+  // Like DefineNamedOwnProperty, but may only be used for the initializing
+  // store of a property of an object literal created from a boilerplate: the
+  // property exists in the object's map and still holds the uninitialized
+  // value.
+  BytecodeArrayBuilder& DefineNamedOwnPropertyInLiteral(
+      Register object, const AstRawString* name, int feedback_slot);
+
   // Set a property keyed by a value in a register, trigger the setters and
   // set traps if necessary. The value to be set should be in the
   // accumulator.
diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc
index aa5d769..b6b5932 100644
--- a/src/interpreter/bytecode-generator.cc
+++ b/src/interpreter/bytecode-generator.cc
@@ -4286,8 +4286,13 @@
           VisitForAccumulatorValue(property->value());
           if (key->IsStringLiteral()) {
             FeedbackSlot slot = feedback_spec()->AddDefineNamedOwnICSlot();
-            builder()->DefineNamedOwnProperty(literal, key->AsRawPropertyName(),
-                                              feedback_index(slot));
+            if (clone_object_spread) {
+              builder()->DefineNamedOwnProperty(
+                  literal, key->AsRawPropertyName(), feedback_index(slot));
+            } else {
+              builder()->DefineNamedOwnPropertyInLiteral(
+                  literal, key->AsRawPropertyName(), feedback_index(slot));
+            }
           } else {
             FeedbackSlot slot = feedback_spec()->AddDefineKeyedOwnICSlot();
             builder()->DefineKeyedOwnProperty(
diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h
index 84fe25f..965fa71 100644
--- a/src/interpreter/bytecodes.h
+++ b/src/interpreter/bytecodes.h
@@ -194,6 +194,9 @@
   V(DefineNamedOwnProperty, ImplicitRegisterUse::kReadAndClobberAccumulator,   \
     OperandType::kReg, OperandType::kConstantPoolIndex,                        \
     OperandType::kFeedbackSlot)                                                \
+  V(DefineNamedOwnPropertyInLiteral,                                           \
+    ImplicitRegisterUse::kReadAndClobberAccumulator, OperandType::kReg,        \
+    OperandType::kConstantPoolIndex, OperandType::kFeedbackSlot)               \
   V(SetKeyedProperty, ImplicitRegisterUse::kReadAndClobberAccumulator,         \
     OperandType::kReg, OperandType::kReg, OperandType::kFeedbackSlot)          \
   V(DefineKeyedOwnProperty, ImplicitRegisterUse::kReadAndClobberAccumulator,   \
diff --git a/src/interpreter/interpreter-generator.cc b/src/interpreter/interpreter-generator.cc
index 19762c5..73eee42 100644
--- a/src/interpreter/interpreter-generator.cc
+++ b/src/interpreter/interpreter-generator.cc
@@ -748,6 +748,17 @@
   SetNamedProperty(Builtin::kDefineNamedOwnIC, NamedPropertyType::kOwn);
 }
 
+// DefineNamedOwnPropertyInLiteral <object> <name_index> <slot>
+//
+// Same as DefineNamedOwnProperty, but only used for the initializing store of
+// a property of an object literal created from a boilerplate, where the
+// property still holds the uninitialized value. Optimizing compilers rely on
+// this.
+IGNITION_HANDLER(DefineNamedOwnPropertyInLiteral,
+                 InterpreterSetNamedPropertyAssembler) {
+  SetNamedProperty(Builtin::kDefineNamedOwnIC, NamedPropertyType::kOwn);
+}
+
 // SetKeyedProperty <object> <key> <slot>
 //
 // Calls the KeyedStoreIC at FeedbackVector slot <slot> for <object> and
diff --git a/src/maglev/maglev-graph-builder.cc b/src/maglev/maglev-graph-builder.cc
index 820b89c..549df76 100644
--- a/src/maglev/maglev-graph-builder.cc
+++ b/src/maglev/maglev-graph-builder.cc
@@ -4512,7 +4512,7 @@
       broker()->dependencies()->DependOnNoSlackTrackingChange(*original_map);
     }
   } else if (access_info.IsFastDataConstant() &&
-             access_mode == compiler::AccessMode::kStore) {
+             access_mode != compiler::AccessMode::kStoreInLiteral) {
     return reducer_.EmitUnconditionalDeopt(DeoptimizeReason::kStoreToConstant);
   }
 
@@ -4821,9 +4821,9 @@
               {receiver, GetConstant(feedback.name()), GetAccumulator()},
               feedback_source);
         case compiler::AccessMode::kDefine:
+        case compiler::AccessMode::kStoreInLiteral:
           return {};
         case compiler::AccessMode::kHas:
-        case compiler::AccessMode::kStoreInLiteral:
           UNREACHABLE();
       }
     }
@@ -7028,6 +7028,16 @@
 
 ReduceResult MaglevGraphBuilder::VisitDefineNamedOwnProperty() {
   // DefineNamedOwnProperty <object> <name_index> <slot>
+  return VisitDefineNamedOwnPropertyHelper(false);
+}
+
+ReduceResult MaglevGraphBuilder::VisitDefineNamedOwnPropertyInLiteral() {
+  // DefineNamedOwnPropertyInLiteral <object> <name_index> <slot>
+  return VisitDefineNamedOwnPropertyHelper(true);
+}
+
+ReduceResult MaglevGraphBuilder::VisitDefineNamedOwnPropertyHelper(
+    bool in_literal) {
   ValueNode* object = LoadRegister(0);
   compiler::NameRef name = GetRefOperand<Name>(1);
   FeedbackSlot slot = GetSlotOperand(2);
@@ -7051,7 +7061,9 @@
     case compiler::ProcessedFeedback::kNamedAccess:
       RETURN_IF_DONE(TryBuildNamedAccess(
           object, object, processed_feedback.AsNamedAccess(), feedback_source,
-          compiler::AccessMode::kDefine, build_generic_access));
+          in_literal ? compiler::AccessMode::kStoreInLiteral
+                     : compiler::AccessMode::kDefine,
+          build_generic_access));
       break;
 
     default:
diff --git a/src/maglev/maglev-graph-builder.h b/src/maglev/maglev-graph-builder.h
index a7f84ac..99e1686 100644
--- a/src/maglev/maglev-graph-builder.h
+++ b/src/maglev/maglev-graph-builder.h
@@ -1363,6 +1363,7 @@
   ReduceResult BuildGetKeyedProperty(
       ValueNode* object, const compiler::FeedbackSource& feedback_source,
       const compiler::ProcessedFeedback& processed_feedback);
+  ReduceResult VisitDefineNamedOwnPropertyHelper(bool in_literal);
   ReduceResult BuildSetKeyedProperty(
       ValueNode* object, ValueNode* index, compiler::AccessMode access_mode,
       const compiler::FeedbackSource& feedback_source,
diff --git a/test/mjsunit/compiler/define-const-data-field-class-field.js b/test/mjsunit/compiler/define-const-data-field-class-field.js
new file mode 100644
index 0000000..af62b64b
--- /dev/null
+++ b/test/mjsunit/compiler/define-const-data-field-class-field.js
@@ -0,0 +1,158 @@
+// Copyright 2026 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 --turbofan
+
+// A DefineNamedOwnProperty into a const field may only keep the field const
+// when the field still holds the uninitialized value, i.e. when the define is
+// the initializing store of an object literal. A define into an already
+// initialized const field must generalize the field to mutable.
+
+// Class field initializer, run on an already initialized object via return
+// override.
+(function TestDefineIntoInitializedConstField() {
+  function H1(a, b) { this.otherProp = a; this.myProp = b;}
+  function H2(a, b) { this.otherProp = a; this.myProp = b;}
+
+  const first = {};
+  const second = {};
+  const holder = new H1(1, first);
+
+  class Base { constructor(o) { return o; } }
+
+  // This function is just used for feeding map information into the optimized function.
+  function feedMap(o) {
+    return o.otherProp;
+  }
+  %PrepareFunctionForOptimization(feedMap);
+
+  // Warm up with both H1 and H2.
+  const dummyH1 = new H1(1, second);
+  const dummyH2 = new H2(1, second);
+  feedMap(dummyH1);
+  feedMap(dummyH2);
+
+  class D extends Base { myProp = (feedMap(this), second); }
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/test/mjsunit/compiler/define-const-data-field-class-field.js b/test/mjsunit/compiler/define-const-data-field-class-field.js
new file mode 100644
index 0000000..af62b64b
--- /dev/null
+++ b/test/mjsunit/compiler/define-const-data-field-class-field.js
@@ -0,0 +1,158 @@
+// Copyright 2026 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 --turbofan
+
+// A DefineNamedOwnProperty into a const field may only keep the field const
+// when the field still holds the uninitialized value, i.e. when the define is
+// the initializing store of an object literal. A define into an already
+// initialized const field must generalize the field to mutable.
+
+// Class field initializer, run on an already initialized object via return
+// override.
+(function TestDefineIntoInitializedConstField() {
+  function H1(a, b) { this.otherProp = a; this.myProp = b;}
+  function H2(a, b) { this.otherProp = a; this.myProp = b;}
+
+  const first = {};
+  const second = {};
+  const holder = new H1(1, first);
+
+  class Base { constructor(o) { return o; } }
+
+  // This function is just used for feeding map information into the optimized function.
+  function feedMap(o) {
+    return o.otherProp;
+  }
+  %PrepareFunctionForOptimization(feedMap);
+
+  // Warm up with both H1 and H2.
+  const dummyH1 = new H1(1, second);
+  const dummyH2 = new H2(1, second);
+  feedMap(dummyH1);
+  feedMap(dummyH2);
+
+  class D extends Base { myProp = (feedMap(this), second); }
+
+  function define(o) {
+    new D(o);
+  }
+  %PrepareFunctionForOptimization(D);
+  %PrepareFunctionForOptimization(define);
+
+  // Warm up define only with H2.
+  for (let i = 0; i < 20; i++) {
+     define(new H2(1, second));
+  }
+
+  %OptimizeFunctionOnNextCall(define);
+  define(new H2(1, second));
+  assertOptimized(define);
+
+  // Assert the good state before we begin the shenanigans.
+  assertTrue(%HasOwnConstDataProperty(holder, "myProp"));
+
+  // This call should invalidate the constness of 'myProp' in H1.
+  define(holder);
+  assertSame(second, holder.myProp);
+  assertFalse(%HasOwnConstDataProperty(holder, "myProp"));
+})();
+
+// Same, but the const field has Smi representation.
+(function TestDefineIntoInitializedConstFieldSmi() {
+  function H1(a, b) { this.otherProp = a; this.myProp = b;}
+  function H2(a, b) { this.otherProp = a; this.myProp = b;}
+
+  const first = 10;
+  const second = 20;
+  const holder = new H1(1, first);
+
+  class Base { constructor(o) { return o; } }
+
+  // This function is just used for feeding map information into the optimized function.
+  function feedMap(o) {
+    return o.otherProp;
+  }
+  %PrepareFunctionForOptimization(feedMap);
+
+  // Warm up with both H1 and H2.
+  const dummyH1 = new H1(1, second);
+  const dummyH2 = new H2(1, second);
+  feedMap(dummyH1);
+  feedMap(dummyH2);
+
+  class D extends Base { myProp = (feedMap(this), second); }
+
+  function define(o) {
+    new D(o);
+  }
+  %PrepareFunctionForOptimization(D);
+  %PrepareFunctionForOptimization(define);
+
+  // Warm up define only with H2.
+  for (let i = 0; i < 20; i++) {
+     define(new H2(1, second));
+  }
+
+  %OptimizeFunctionOnNextCall(define);
+  define(new H2(1, second));
+  assertOptimized(define);
+
+  // Assert the good state before we begin the shenanigans.
+  assertTrue(%HasOwnConstDataProperty(holder, "myProp"));
+
+  // This call should invalidate the constness of 'myProp' in H1.
+  define(holder);
+  assertSame(second, holder.myProp);
+  assertFalse(%HasOwnConstDataProperty(holder, "myProp"));
+})();
+
+// Same, but the const field has Double representation.
+(function TestDefineIntoInitializedConstFieldDouble() {
+  function H1(a, b) { this.otherProp = a; this.myProp = b;}
+  function H2(a, b) { this.otherProp = a; this.myProp = b;}
+
+  const first = 1.5;
+  const second = 2.5;
+  const holder = new H1(1, first);
+
+  class Base { constructor(o) { return o; } }
+
+  // This function is just used for feeding map information into the optimized function.
+  function feedMap(o) {
+    return o.otherProp;
+  }
+  %PrepareFunctionForOptimization(feedMap);
+
+  // Warm up with both H1 and H2.
+  const dummyH1 = new H1(1, second);
+  const dummyH2 = new H2(1, second);
+  feedMap(dummyH1);
+  feedMap(dummyH2);
+
+  class D extends Base { myProp = (feedMap(this), second); }
+
+  function define(o) {
+    new D(o);
+  }
+  %PrepareFunctionForOptimization(D);
+  %PrepareFunctionForOptimization(define);
+
+  // Warm up define only with H2.
+  for (let i = 0; i < 20; i++) {
+     define(new H2(1, second));
+  }
+
+  %OptimizeFunctionOnNextCall(define);
+  define(new H2(1, second));
+  assertOptimized(define);
+
+  // Assert the good state before we begin the shenanigans.
+  assertTrue(%HasOwnConstDataProperty(holder, "myProp"));
+
+  // This call should invalidate the constness of 'myProp' in H1.
+  define(holder);
+  assertSame(second, holder.myProp);
+  assertFalse(%HasOwnConstDataProperty(holder, "myProp"));
+})();
diff --git a/test/mjsunit/maglev/define-const-data-field-class-field.js b/test/mjsunit/maglev/define-const-data-field-class-field.js
new file mode 100644
index 0000000..59c799c
--- /dev/null
+++ b/test/mjsunit/maglev/define-const-data-field-class-field.js
@@ -0,0 +1,145 @@
+// Copyright 2026 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 --maglev
+
+// A DefineNamedOwnProperty into a const field may only keep the field const
+// when the field still holds the uninitialized value, i.e. when the define is
+// the initializing store of an object literal. A define into an already
+// initialized const field must generalize the field to mutable.
+
+// Class field initializer, run on an already initialized object via return
+// override. The define site is megamorphic and has never seen an H; Maglev
+// knows the receiver's map from the preceding load. The define must generalize
+// the field.
+
+(function TestDefineIntoInitializedConstField() {
+  function H(v) { this.myProp = v; }
+  const first = {};
+  const second = {};
+  const holder = new H(first);
+  const other = new H(second);
+
+  class Base { constructor(o) { return o; } }
+  class D extends Base { myProp = second; }
+
+  // Make the define site megamorphic without ever seeing an H.
+  for (let i = 0; i < 30; i++) {
+    const o = {};
+    o["p" + i] = i;
+    new D(o);
+  }
+
+  function define(o, constructD) {
+    const t = o.myProp;  // Known maps for o: {H, literal}.
+    if (constructD) new D(o);
+    return t;
+  }
+  %PrepareFunctionForOptimization(define);
+  // One call with an H to get its map into the load feedback, without
+  // constructing D on it (that would generalize H.myProp in the runtime).
+  define(other, false);
+  // The other calls construct D, so that its call frequency stays high enough
+  // for Maglev to inline it.
+  for (let i = 0; i < 30; i++) define({myProp: second}, true);
+  %OptimizeMaglevOnNextCall(define);
+  define({myProp: second}, true);
+  assertMaglevved(define);
+
+  // Assert the good state before we begin the shenanigans.
+  assertTrue(%HasOwnConstDataProperty(holder, "myProp"));
+
+  // This call should invalidate the constness of 'myProp' in H.
+  assertSame(first, define(holder, true));
+  assertSame(second, holder.myProp);
+  assertFalse(%HasOwnConstDataProperty(holder, "myProp"));
+})();
+
+// Same, but the const field has Smi representation. The representation is
+// determined by the first store in H; there is no runtime function to assert
+// it, so the test relies on 1 giving a Smi field.
+(function TestDefineIntoInitializedConstSmiField() {
+  function H(v) { this.myProp = v; }
+  const first = 1;
+  const second = 2;
+  const holder = new H(first);
+  const other = new H(second);
+
+  class Base { constructor(o) { return o; } }
+  class D extends Base { myProp = second; }
+
+  // Make the define site megamorphic without ever seeing an H.
+  for (let i = 0; i < 30; i++) {
+    const o = {};
+    o["p" + i] = i;
+    new D(o);
+  }
+
+  function define(o, constructD) {
+    const t = o.myProp;  // Known maps for o: {H, literal}.
+    if (constructD) new D(o);
+    return t;
+  }
+  %PrepareFunctionForOptimization(define);
+  // One call with an H to get its map into the load feedback, without
+  // constructing D on it (that would generalize H.myProp in the runtime).
+  define(other, false);
+  // The other calls construct D, so that its call frequency stays high enough
+  // for Maglev to inline it.
+  for (let i = 0; i < 30; i++) define({myProp: second}, true);
+  %OptimizeMaglevOnNextCall(define);
+  define({myProp: second}, true);
+  assertMaglevved(define);
+
+  // Assert the good state before we begin the shenanigans.
+  assertTrue(%HasOwnConstDataProperty(holder, "myProp"));
+
+  // This call should invalidate the constness of 'myProp' in H.
+  assertSame(first, define(holder, true));
+  assertSame(second, holder.myProp);
+  assertFalse(%HasOwnConstDataProperty(holder, "myProp"));
+})();
+
+// Same, but the const field has Double representation.
+(function TestDefineIntoInitializedConstDoubleField() {
+  function H(v) { this.myProp = v; }
+  const first = 1.5;
+  const second = 2.5;
+  const holder = new H(first);
+  const other = new H(second);
+
+  class Base { constructor(o) { return o; } }
+  class D extends Base { myProp = second; }
+
+  // Make the define site megamorphic without ever seeing an H.
+  for (let i = 0; i < 30; i++) {
+    const o = {};
+    o["p" + i] = i;
+    new D(o);
+  }
+
+  function define(o, constructD) {
+    const t = o.myProp;  // Known maps for o: {H, literal}.
+    if (constructD) new D(o);
+    return t;
+  }
+  %PrepareFunctionForOptimization(define);
+  // One call with an H to get its map into the load feedback, without
+  // constructing D on it (that would generalize H.myProp in the runtime).
+  define(other, false);
... (truncated)
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.