FlawOpen

flawopen.com/Teardowns/cve-2024-4947-chrome-v8-type-confusion

● CVE-2024-4947 · CVSS 9.6 · Critical
FlawOpen Security Research

CVE-2024-4947: Chrome V8 Maglev Type Confusion

Maglev’s store bug let a crafted page trigger type confusion and code execution inside Chrome’s renderer sandbox. Google confirmed exploitation.[1], [3], [2], [8]

💡 Plain English Explainer (ELI5)

A module export is like a read-only label on a shared library shelf: JavaScript cannot replace it with an ordinary write. Maglev treated that forbidden write like a normal object-field update. The compiler then used the wrong layout rule for the object, so later code interpreted its data using the wrong type. Chrome fixed the compiler’s classification and store handling.[4], [2]

Core Concepts & Subsystem Terms

Maglev JIT
V8’s optimizing compiler that turns frequently executed JavaScript paths into machine code.[7]
Module namespace object
The object exposed by an ECMAScript module namespace. Its exported bindings are not ordinary writable object properties; a store must follow the specification’s special semantics.[4], [2]
PropertyAccessInfo
V8 compiler metadata describing what kind of property access is valid and how it should be handled.[2]
Type confusion
A flaw where compiled code treats a value or object as having a different representation than it actually has.[6], [3]

Framework mappings

Root Cause Analysis

The Maglev graph builder could receive PropertyAccessInfo for a store to a module namespace export and route it through the ordinary data-field store path. Module namespace writes have special ECMAScript semantics: they fail or have no effect, rather than updating a normal property slot. The resulting field access used an invalid layout assumption and could corrupt object metadata, causing type confusion. The upstream fix has two parts: AccessInfo rejects stores to module namespace objects, and the Maglev builder switches on the access kind so a module export cannot fall through to TryBuildStoreField. Google’s advisory confirms code execution inside Chrome’s sandbox and exploitation in the wild; it does not establish a sandbox escape or host compromise from this CVE alone.[2], [4], [5], [3]

Step-by-Step Attack Flow

Step 1

A crafted page reaches the browser

A remote attacker can serve crafted HTML, but the user must visit or otherwise load the page in a vulnerable Chrome build. Google confirmed that an exploit existed in the wild.[1], [3], [8]

Step 2

JavaScript exercises the Maglev store path

The page’s JavaScript drives an optimized assignment involving an ECMAScript module namespace export. Such exports are not ordinary writable data fields.[4], [2], [5]

Step 3

The compiler uses the wrong access kind

The vulnerable Maglev path treated module-export access information like a normal field store. The emitted operation could write using the wrong object layout, producing type confusion.[2], [5]

Step 4

Impact remains inside the renderer sandbox

The CVE record describes arbitrary code execution inside Chrome’s sandbox. A separate sandbox escape would be needed to claim execution outside that boundary; CVE-2024-4947 alone does not establish one.[3], [1]

Source Code: Flaw vs. Secure Implementation

This excerpt is from V8 commit 164b664ce2b203515c05f28633bf0a10c4c7c32d. The full fix also makes AccessInfo return Invalid for any store to a module namespace object. The explicit switch prevents kModuleExport from being treated as a data field; it mirrors the upstream patch rather than a speculative runtime guard.[2], [5]

Source excerpt: src/maglev/maglev-graph-builder.cc

UNPATCHED FLAW
// BEFORE FIX — an accessor was handled specially, but every other access
// was assumed to be a data-field store. Module exports were not excluded.
if (access_info.IsFastAccessorConstant()) {
  return TryBuildPropertySetterCall(access_info, receiver,
                                    GetAccumulatorTagged());
}
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
ReduceResult result = TryBuildStoreField(access_info, receiver, access_mode);
if (result.IsDone()) {
  RecordKnownProperty(receiver, name,
                      current_interpreter_frame_.accumulator(),
                      AccessInfoGuaranteedConst(access_info), access_mode);
  return result;
}
return ReduceResult::Fail();
HARDENED SECURE PATCH
// AFTER FIX — only valid store kinds use the field-store path.
switch (access_info.kind()) {
  case compiler::PropertyAccessInfo::kFastAccessorConstant:
    return TryBuildPropertySetterCall(access_info, receiver,
                                      GetAccumulatorTagged());
  case compiler::PropertyAccessInfo::kDataField:
  case compiler::PropertyAccessInfo::kFastDataConstant: {
    ReduceResult result = TryBuildStoreField(access_info, receiver, access_mode);
    if (result.IsDone()) {
      RecordKnownProperty(receiver, name,
                          current_interpreter_frame_.accumulator(),
                          AccessInfoGuaranteedConst(access_info), access_mode);
      return result;
    }
    return ReduceResult::Fail();
  }
  case compiler::PropertyAccessInfo::kInvalid:
  case compiler::PropertyAccessInfo::kNotFound:
  case compiler::PropertyAccessInfo::kDictionaryProtoDataConstant:
  case compiler::PropertyAccessInfo::kDictionaryProtoAccessorConstant:
  case compiler::PropertyAccessInfo::kModuleExport:
  case compiler::PropertyAccessInfo::kStringLength:
    UNREACHABLE();
}

Engineering & System Hardening Checklist

Sources