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

● CVE-2024-4947 · CVSS 9.8 · Critique
Recherche · FlawOpen

CVE-2024-4947: Chrome V8 JIT Compiler Type Confusion Teardown

Analyse technique détaillée du code source et mesures de durcissement pour vulnerabilidade : How property getter transitions in V8's Turbofan JIT compiler tricked optimized native code into reading raw pointers as floating-point numbers, yielding in-the-wild remote code execution.

💡 Explication en Langage Simple (ELI5)

Analogie concrète : V8 makes JavaScript super fast by making assumptions. If you pass an array of numbers to a function 1,000 times, V8 turns that function into machine code that skips all safety checks. An attacker creates a sneaky property that changes the array from 'numbers' to 'object pointers' right in the middle of execution. The machine code treats an object's memory address as a number, letting the attacker read and write raw computer memory.

Concepts Clés et Termes

Turbofan JIT
Le compilateur d'optimisation du moteur V8 de Google qui convertit le bytecode JavaScript en code machine haute performance spécifique à l'architecture.
Hidden Class / Map
Objet de métadonnées interne de V8 suivant la disposition, les noms de propriétés et les types d'éléments des objets JavaScript en mémoire.
Type Confusion
Faille de corruption de mémoire survenant lorsque le code traite une région mémoire initialisée en tant que Type A comme s'il s'agissait du Type B.
Map Deprecating Transition
Lorsque la modification dynamique d'une propriété d'objet altère sa structure interne, forçant le code compilé par le JIT à se désoptimiser.

Analyse de Cause Racine

La cause fondamentale provient de paramètres de limites non validés dans les systèmes open source, permettant une désynchronisation d'état et le contournement des contrôles de sécurité.

Déroulement de l'Attaque Étape par Étape

Step 1

1. JIT Warm-Up

The attacker runs a loop passing an array of floating-point numbers (PACKED_DOUBLE_ELEMENTS) to a function until Turbofan compiles it to unverified native assembly.

Step 2

2. Prototype Getter Interception

Inside a customized property getter, the attacker alters the array layout by storing an object reference, changing the map to PACKED_ELEMENTS.

Step 3

3. Unchecked Native Execution

Because Turbofan omitted a dynamic map transition check, the compiled native loop continues to read the array as raw 64-bit IEEE floats.

Step 4

4. Arbitrary Read/Write Primitive

The float values represent raw pointers. The attacker constructs an addrof (read address) and fakeobj (corrupt address) primitive to break out of the V8 sandbox.

Code Source : Vulnérable vs Sécurisé

✕ IMPLÉMENTATION VULNÉRABLE
// VULNERABLE: Simplified C++ Turbofan Graph Optimization (v8/src/compiler/)
// The optimizer assumed Map state remained unchanged across property accesses!

Reduction JSNativeContextSpecialization::ReduceNamedAccess(Node* node) {
  MapRef receiver_map = GetReceiverMap(node);
  
  // VULNERABILITY:
  // If property access invokes a custom JS getter that mutates the object's Map,
  // Turbofan fails to emit an effect-dependent map verification node!
  if (receiver_map.is_stable()) {
    // Relies on static stability without guarding against dynamic in-getter mutation
    return BuildPropertyLoad(node, receiver_map); 
  }
  
  return NoChange();
}
✓ PATCH SÉCURISÉ ET ROBUSTE
// SECURE: Explicit Map Transition Guard Insertion in Turbofan Graph
Reduction JSNativeContextSpecialization::ReduceNamedAccess(Node* node) {
  MapRef receiver_map = GetReceiverMap(node);

  // FIX: Verify map stability AND emit runtime type transition guard
  if (receiver_map.is_stable()) {
    dependencies()->DependOnStableMap(receiver_map);

    // Explicitly insert dynamic Map check node before unboxing properties
    Node* effect = NodeProperties::GetEffectInput(node);
    Node* check = graph()->NewNode(
        simplified()->CheckMaps(CheckMapsFlag::kNone, 
                               ZoneHandleSet<Map>(receiver_map.object())),
        receiver, effect, control);

    // If an in-flight getter changes the object layout, force JIT deoptimization!
    NodeProperties::ReplaceEffectInput(node, check);
    return BuildPropertyLoad(node, check, receiver_map);
  }

  return NoChange();
}

Liste de Contrôle de Sécurité pour l'Ingénierie