CVE-2026-1862
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchsrc/maglev/maglev-ir.h |
modified |
Files Changed
src/maglev/maglev-ir.ccsrc/maglev/maglev-ir.h
Patch
From 4508b5dfb26e86f975fc57cf04350d67071fe98e Mon Sep 17 00:00:00 2001 From: Victor Gomes <[email protected]> Date: Fri, 30 Jan 2026 15:18:32 +0100 Subject: [PATCH] [maglev] Module variables can be the hole Module variables are lowered in Maglev to LoadTaggedField(cell, Cell:kValueOffset). Drive-by: order opcodes alphabetically in CanBeTheHoleValue. Fixed: 479726070 Change-Id: I2be5752906cf2ec8fdb4df497724a4d9ad55648d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7534881 Auto-Submit: Victor Gomes <[email protected]> Reviewed-by: Leszek Swirski <[email protected]> Commit-Queue: Leszek Swirski <[email protected]> Commit-Queue: Victor Gomes <[email protected]> Cr-Commit-Position: refs/heads/main@{#105008} --- diff --git a/src/maglev/maglev-ir.cc b/src/maglev/maglev-ir.cc index 47c5e09..dd35ce7 100644 --- a/src/maglev/maglev-ir.cc +++ b/src/maglev/maglev-ir.cc @@ -669,6 +669,13 @@ if (const RootConstant* cst = TryCast<RootConstant>()) { return ToTribool(cst->index() == RootIndex::kTheHoleValue); } + if (const LoadTaggedField* load = TryCast<LoadTaggedField>()) { + // Modules variables can be the hole. + if (load->offset() == Cell::kValueOffset) { + return Tribool::kMaybe; + } + return Tribool::kFalse; + } if (const LoadFixedArrayElement* load = TryCast<LoadFixedArrayElement>()) { if (load->load_type() != LoadType::kUnknown) { return Tribool::kFalse; diff --git a/src/maglev/maglev-ir.h b/src/maglev/maglev-ir.h index 11bc7d48..f4c0ff8 100644 --- a/src/maglev/maglev-ir.h +++ b/src/maglev/maglev-ir.h @@ -733,17 +733,18 @@ constexpr bool CanBeTheHoleValue(Opcode opcode) { switch (opcode) { - case Opcode::kInitialValue: - case Opcode::kCallRuntime: // TODO(victorgomes): Should we have a list of builtins that could // return the hole? case Opcode::kCallBuiltin: + case Opcode::kCallRuntime: case Opcode::kGeneratorRestoreRegister: - case Opcode::kRootConstant: + case Opcode::kInitialValue: case Opcode::kLoadContextSlot: case Opcode::kLoadContextSlotNoCells: case Opcode::kLoadFixedArrayElement: + case Opcode::kLoadTaggedField: case Opcode::kPhi: + case Opcode::kRootConstant: return true; default: return false;
Original Bug Report
Hole leak in MaglevGraphBuilder
VULNERABILITY DETAILS
Summary
MaglevGraphBuilder::VisitThrowReferenceErrorIfHole incorrectly eliminates TDZ checks for module variables because CanBeTheHoleValue does not include LoadTaggedField in its whitelist. This causes the_hole_value (HOLE_TYPE) to leak into JS when accessing uninitialized module bindings, leading to type confusion. This issue is similar to crbug.com/450618029
Suggested Fix
Add Opcode::kLoadTaggedField to the CanBeTheHoleValue() whitelist, or special-case Cell::kValueOffset reads to return Tribool::kMaybe to prevent hole check elimination for module variable accesses.
Attached fix.diff which adds kLoadTaggedField to the CanBeTheHoleValue() whitelist:
diff --git a/src/maglev/maglev-ir.h b/src/maglev/maglev-ir.h
--- a/src/maglev/maglev-ir.h
+++ b/src/maglev/maglev-ir.h
@@ -734,6 +734,9 @@ constexpr bool CanBeTheHoleValue(Opcode opcode) {
switch (opcode) {
case Opcode::kInitialValue:
case Opcode::kCallRuntime:
+ case Opcode::kLoadTaggedField:
// TODO(victorgomes): Should we have a list of builtins that could
// return the hole?
case Opcode::kCallBuiltin:
Details
When accessing module variables, Ignition generates the following bytecode sequence:
LdaModuleVariableThrowReferenceErrorIfHole "<name>"Return
The ThrowReferenceErrorIfHole bytecode is critical for TDZ enforcement and must be preserved to prevent the_hole_value from leaking.
In MaglevGraphBuilder::VisitLdaModuleVariable, module variable accesses are compiled to read from a Cell via LoadTaggedField(Cell::kValueOffset). The issue is that ValueNode::IsTheHole returns kFalse for LoadTaggedField nodes because CanBeTheHoleValue does not include Opcode::kLoadTaggedField in its whitelist:
constexpr bool CanBeTheHoleValue(Opcode opcode) {
switch (opcode) {
case Opcode::kInitialValue:
case Opcode::kCallRuntime:
// TODO(victorgomes): Should we have a list of builtins that could
// return the hole?
case Opcode::kCallBuiltin:
case Opcode::kGeneratorRestoreRegister:
case Opcode::kRootConstant:
case Opcode::kLoadContextSlot:
case Opcode::kLoadContextSlotNoCells:
case Opcode::kLoadFixedArrayElement:
case Opcode::kPhi:
return true;
default:
return false;
}
}
As a result, when MaglevGraphBuilder::VisitThrowReferenceErrorIfHole checks IsTheHole() and gets kFalse, it skips inserting the hole check entirely. Additionally, MaglevGraphOptimizer::VisitThrowReferenceErrorIfHole removes any existing ThrowReferenceErrorIfHole nodes when IsTheHole() == kFalse.
ReduceResult MaglevGraphBuilder::VisitThrowReferenceErrorIfHole() {
// ThrowReferenceErrorIfHole <variable_name>
compiler::NameRef name = GetRefOperand<Name>(0);
ValueNode* value = GetAccumulator();
switch (value->IsTheHole()) {
case Tribool::kTrue:
return BuildThrow(Throw::kThrowAccessedUninitializedVariable,
GetConstant(name));
case Tribool::kFalse:
return ReduceResult::Done();
case Tribool::kMaybe:
DCHECK(value->is_tagged());
return AddNewNode<ThrowReferenceErrorIfHole>({value}, name);
}
}
This causes the_hole_value to leak into JavaScript, which then triggers crashes when the value is used (e.g., in Map.delete() or JSON.stringify()).
VERSION
V8 commit: 5853269075baaf304cd5be09d941a6c3e9671847
V8 Version: 14.6.101
REPRODUCTION CASE
Build args on commit 5853269075baaf304cd5be09d941a6c3e9671847:
is_component_build = false
is_debug = false
v8_enable_backtrace = true
dcheck_always_on = true
is_asan=true
v8_static_library=true
Run ./d8 --module --allow-natives-syntax poc.js, you would observe the DCHECK failure as the stack.txt shows.
- https://crbug.com/450618029
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/maglev/maglev-graph-builder.cc;l=16443
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/maglev/maglev-graph-builder.cc;l=7564
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/maglev/maglev-graph-optimizer.cc;l=939
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/maglev/maglev-ir.cc;l=667
- https://source.chromium.org/chromium/chromium/src/+/main:v8/src/maglev/maglev-ir.h;l=734