Medium firefox Memory Corruption 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionInternally found bugs present in Firefox 154. Some of these bugs showed evidence of memory corruption or another security-relevant defect and we presume that with enough effort some of these could have been exploited.
ComponentSpiderMonkey
Bug ClassMemory Corruption
Tracker2029421
Fix commit7e565874c7d4 (firefox) +29/-0
CISA KEVNot listed
CreditedAlexandre Poirot, Christian Holler, Sebastian Hengst and the Mozilla Fuzzing Team
Disclosed2026-09-01

Changed Functions

FunctionChangeNotes
for
js/src/intgemm/IntegerGemmIntrinsic.cpp
modified
if
js/src/intgemm/IntegerGemmIntrinsic.cpp
modified
for
js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
modified

Files Changed

  • js/src/intgemm/IntegerGemmIntrinsic.cpp
  • js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
diff --git a/js/src/intgemm/IntegerGemmIntrinsic.cpp b/js/src/intgemm/IntegerGemmIntrinsic.cpp
index f1c400c5818..94f780009eb 100644
--- a/js/src/intgemm/IntegerGemmIntrinsic.cpp
+++ b/js/src/intgemm/IntegerGemmIntrinsic.cpp
@@ -457,6 +457,16 @@ int32_t js::intgemm::IntrI8SelectColumnsOfB(wasm::Instance* instance,
   const uint32_t* colIndexListPtr =
       reinterpret_cast<const uint32_t*>(&memBase[colIndexList]);
   int8_t* outputPtr = reinterpret_cast<int8_t*>(&memBase[output]);
+
+  // Every selected column index must reference a valid column of B. Otherwise
+  // SelectColumnsB would read outside the bounds-checked input matrix, since it
+  // uses each index to compute an offset into inputMatrixBPrepared.
+  for (uint32_t i = 0; i < sizeColIndexList; i++) {
+    if (colIndexListPtr[i] >= colsB) {
+      return -1;
+    }
+  }
+
   AutoProfilerMarker marker(cx->runtime()->geckoProfiler(),
                             "integemm::SelectColumnsB",
                             "rowsB: {} colsB: {} sizecolList: {}, sizeB: {}",
diff --git a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
index dcdefa62947..ab5a57c0d20 100644
--- a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
+++ b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
@@ -60,6 +60,24 @@ function testOutOfBounds() {
   assertErrorMessage(() => int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, outOfBound), WebAssembly.RuntimeError, /index out of bounds/);
 }
 
+function testInvalidColumnIndex() {
+  // The bound checks above only cover the location of the colIndexList array,
+  // not the column indices it contains. Each index must be a valid column of B
+  // (< colsB); otherwise SelectColumnsB reads out of bounds of the input matrix
+  // and copies host memory into the output buffer.
+  let colIndex = new Uint32Array(memory.buffer);
+  let base = VALID.colIndexList >> 2;
+
+  for (let wild of [VALID.cols, 0x20000000, 0xffffffff]) {
+    for (let i = 0; i < VALID.sizeColIndexList; i++) colIndex[base + i] = 0;
+    colIndex[base + 3] = wild;
+    assertErrorMessage(() => int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, VALID.output), WebAssembly.RuntimeError, /index out of bounds/);
+  }
+
+  // Restore valid indices so the successful-call test below still passes.
+  for (let i = 0; i < VALID.sizeColIndexList; i++) colIndex[base + i] = 0;
+}
+
 function testSuccessfulCall() {
   // We just test that with valid arguments the intrinsic executes without any error
   int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, VALID.output);
@@ -68,6 +86,7 @@ function testSuccessfulCall() {
 testInvalidSize();
 testInvalidAlignment();
 testOutOfBounds();
+testInvalidColumnIndex();
 testSuccessfulCall();
 `
 
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
index dcdefa62947..ab5a57c0d20 100644
--- a/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
+++ b/js/src/jit-test/tests/wasm/builtin-modules/integer-gemm/I8SelectColumnsOfB.js
@@ -60,6 +60,24 @@ function testOutOfBounds() {
   assertErrorMessage(() => int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, outOfBound), WebAssembly.RuntimeError, /index out of bounds/);
 }
 
+function testInvalidColumnIndex() {
+  // The bound checks above only cover the location of the colIndexList array,
+  // not the column indices it contains. Each index must be a valid column of B
+  // (< colsB); otherwise SelectColumnsB reads out of bounds of the input matrix
+  // and copies host memory into the output buffer.
+  let colIndex = new Uint32Array(memory.buffer);
+  let base = VALID.colIndexList >> 2;
+
+  for (let wild of [VALID.cols, 0x20000000, 0xffffffff]) {
+    for (let i = 0; i < VALID.sizeColIndexList; i++) colIndex[base + i] = 0;
+    colIndex[base + 3] = wild;
+    assertErrorMessage(() => int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, VALID.output), WebAssembly.RuntimeError, /index out of bounds/);
+  }
+
+  // Restore valid indices so the successful-call test below still passes.
+  for (let i = 0; i < VALID.sizeColIndexList; i++) colIndex[base + i] = 0;
+}
+
 function testSuccessfulCall() {
   // We just test that with valid arguments the intrinsic executes without any error
   int8_select_columns_of_b(VALID.input, VALID.rows, VALID.cols, VALID.colIndexList, VALID.sizeColIndexList, VALID.output);
@@ -68,6 +86,7 @@ function testSuccessfulCall() {
 testInvalidSize();
 testInvalidAlignment();
 testOutOfBounds();
+testInvalidColumnIndex();
 testSuccessfulCall();
 `
Loading diff…