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.
-
Incorrect Reset of
heap_access_shift_position_:- In the original code, the
heap_access_shift_position_was being reset tokNoHeapAccessShiftbefore the recursiveAdditiveExpressioncall. 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.
- In the original code, the
-
Fixed Code:
- The fix involves moving the reset of
heap_access_shift_position_after the recursive call toAdditiveExpression, 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.
--- 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; \ }
- The fix involves moving the reset of
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));-
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-overflowoccurs. - RSP (stack pointer) and registers indicate a valid crash with the stack returning to unexpected locations.
- The registers show standard call stack corruption after the
- The patch fixes the issue by correctly resetting
heap_access_shift_position_after the recursive call toAdditiveExpression. 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.
- Fixed Source Code: asm-parser.cc
- Patch Commit: Fix Commit
- PoC: 485152421.js