Skip to content

Instantly share code, notes, and snippets.

@xv0nfers
Last active February 20, 2026 16:38
Show Gist options
  • Select an option

  • Save xv0nfers/6cd445749708a98ffc91e63cd28e0d53 to your computer and use it in GitHub Desktop.

Select an option

Save xv0nfers/6cd445749708a98ffc91e63cd28e0d53 to your computer and use it in GitHub Desktop.
Nested shift state confusion generates invalid Wasm

Problem Summary:

The issue 485152421 stems from an incorrect handling of nested shift expressions within the ASM.js parsing code, leading to invalid Wasm modules being generated. This bug results in heap-buffer-overflows during the execution of certain Wasm code, particularly those that involve memory access manipulation through shift operations.

Root Cause:

  1. Incorrect Reset of heap_access_shift_position_:

    • In the original code, the heap_access_shift_position_ was being reset to kNoHeapAccessShift before the recursive AdditiveExpression call. This caused the shift position to be improperly set when dealing with nested shift expressions.
    • This error was critical in scenarios where heap memory access was involved, as it allowed invalid memory writes, leading to heap-buffer-overflows.
  2. Fixed Code:

    • The fix involves moving the reset of heap_access_shift_position_ after the recursive call to AdditiveExpression, ensuring that the shift position is correctly set only after the expression is evaluated. This prevents the erroneous reset during the evaluation of nested expressions.

    Code Diff:

    --- a/src/asmjs/asm-parser.cc
    +++ b/src/asmjs/asm-parser.cc
    @@ -1892,7 +1892,6 @@
    #define HANDLE_CASE(op, opcode, name, result)                        \
      case TOK(op): {                                                    \
        EXPECT_TOKENn(TOK(op));                                          \
    -    heap_access_shift_position_ = kNoHeapAccessShift;                \
        AsmType* b = nullptr;                                            \
        RECURSEn(b = AdditiveExpression());                              \
        if (!(a->IsA(AsmType::Intish()) && b->IsA(AsmType::Intish()))) { \
    @@ -1900,6 +1899,8 @@
        }                                                                \
        current_function_builder_->Emit(kExpr##opcode);                  \
        a = AsmType::result();                                           \
    +    /* Must happen after the RECURSE call to unset its state! */     \
    +    heap_access_shift_position_ = kNoHeapAccessShift;                \
        continue;                                                        \
      }

PoC:

The PoC demonstrates how a malformed Wasm module triggers the issue when trying to execute the shift operation in the presence of the bug:

// 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.
function AsmModule(stdlib, foreign, heap) {
  "use asm";
  var HEAP32 = new stdlib.Int32Array(heap);
  function g() { return 1.25; }
  function f(a,b,c) {
    a = a | 0;
    b = b | 0;
    c = c | 0;
    HEAP32[a << (b >> 2) + ~~+g()] = c;
    return c | 0;
  }
  return {f:f};
}
var heap = new ArrayBuffer(0x10000);
var m = AsmModule({Int32Array:Int32Array}, {}, heap);
assertEquals(3, m.f(1,2,3));

Debug Insights

  • Error Context:

    • The crash occurred due to an AddressSanitizer heap-buffer-overflow, with the read of size 8 bytes at a location just beyond the heap allocation.
    • Stack traces indicate that this overflow happened during the execution of Wasm code involving the nested shift expressions.
  • Backtrace:

    #0  0x55555be2952c in signature () at ../../v8/src/wasm/wasm-module.h:848
    #1  Validate () at ../../v8/src/wasm/function-body-decoder-impl.h:2149
    #2  DecodeBlockImpl () at ../../v8/src/wasm/function-body-decoder-impl.h:3539
    #3  Decode () at ../../v8/src/wasm/function-body-decoder-impl.h:3536
  • Registers and Stack:

    • The registers show standard call stack corruption after the heap-buffer-overflow occurs.
    • RSP (stack pointer) and registers indicate a valid crash with the stack returning to unexpected locations.

Mitigation Assessment:

  • The patch fixes the issue by correctly resetting heap_access_shift_position_ after the recursive call to AdditiveExpression. This ensures that the state of heap access during nested shifts is handled correctly.
  • However, further testing and code coverage improvements are recommended, especially in cases involving more complex nested expressions or interdependencies between shift operations and heap access.

Files and Diff References:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment