CVE-2026-3920
Overview
Background
- `ruy`
- a matrix-multiplication library used by TensorFlow Lite CPU kernels; per the advisory the bug surfaces through WebML, and the link from WebNN to
ruyvia the TFLite CPU backend is(inferred)since the diff touches onlyruy. - `MatLayout` / `FlatSize`
MatLayoutdescribes a matrix’srows,cols,strideandorder;FlatSizereturns the number of elements spanned (stride * outerdim) and is used to size and bound matrix buffers.- Packing
- before multiplication,
ruycopies source matrix blocks into a “packed” buffer in kernel-friendly order;pack_avx2_fma.ccholds the x86 AVX2/FMA packing routines, which spill trailing rows through atrailing_bufandmemcpythem intopacked_ptr. - 32-bit index overflow
- multiplying two
intvalues whose product exceedsINT_MAXis undefined/wraps, so anintintermediate can yield a small or negative size/offset even when the result is later stored in a 64-bitstd::ptrdiff_t.
Root Cause Analysis
In ruy::FlatSize, outerdim was declared int, so layout.stride * outerdim could be evaluated in 32-bit arithmetic and overflow for large matrices before being returned as std::ptrdiff_t, producing an undersized element count. In the AVX2/FMA packing routine shown in the pack_avx2_fma.cc hunk (its function name is not in the hunk), trailing_rows and non_trailing_rows were int, so the destination offset packed_ptr + kPackCols * non_trailing_rows was computed as an int product that can overflow for large src_rows, letting the subsequent memcpy of trailing_buf land outside the packed buffer. The invariant violated is that buffer sizes and pointer offsets derived from matrix dimensions must be computed in a type wide enough to hold them.
The patch widens these locals to std::ptrdiff_t (and adds <cstddef>), so the multiplications are done in 64-bit arithmetic on 64-bit targets; no bounds checks are added or relaxed. The diff does not show the callers, the allocation that uses FlatSize, or the WebNN code that feeds dimensions into ruy, so where the out-of-bounds access lands is (inferred).
int intermediates that could overflow for large dimensions; the fix uses std::ptrdiff_t so size and offset arithmetic does not wrap.Attack Path
- Build a large ML graph
Web content uses the WebML (WebNN) API to construct an operation (e.g., a matrix multiply / fully connected layer) whose dimensions make
stride * outerdimorkPackCols * non_trailing_rowsexceedINT_MAX; the specific WebNN operator and dimension checks involved are(inferred). - Dispatch to the CPU backend
The graph runs on a TFLite CPU path that calls into
ruyon an x86 machine selecting the AVX2/FMA packing code(inferred). - Overflowed size/offset
FlatSizereturns a wrapped element count and/or the packing routine computes a wrappedpacked_ptroffset in 32-bit arithmetic. - Out-of-bounds memory access
The trailing-row
memcpywrites at a wrong offset relative to the packed buffer, or buffers sized fromFlatSizeare too small for later accesses; the exact corrupted memory is(inferred).
Impact Assessment
memcpy write in ruy packing and an undersized size computation, while the process it executes in (the process hosting WebNN’s CPU inference) and any escalation to code execution are (inferred). Preconditions: the WebNN API must be available to the page (whether it is enabled by default is not shown in the diff), the CPU/TFLite backend must route the operation into ruy on x86 hardware with AVX2/FMA, and the tensor dimensions must be large enough to overflow 32-bit arithmetic, which may also require that upstream size validation and memory limits permit such shapes (inferred).Changed Functions
| Function | Change | Notes |
|---|---|---|
ifruy/pack_avx2_fma.cc |
modified |
Files Changed
ruy/mat.hruy/pack_avx2_fma.cc
Audit Directions
- `int` intermediates in size mathGrep
ruy(otherpack_*.ccvariants such as AVX-512, AVX, NEON) and TFLite/XNNPACK kernels forintlocals multiplied by strides or pack widths before being used as pointer offsets or allocation sizes. - Dimension limits at the WebNN boundaryCheck that WebNN graph validation bounds operand dimensions and checked-multiplies element counts before handing shapes to third-party CPU backends.
- Return-type widening masks overflowLook for functions returning
std::ptrdiff_t/size_twhose expressions are evaluated entirely inint(asFlatSizewas), since the wide return type gives a false sense of safety.
Patch
From c74e83f433f56aa88465486c5be185369142e61e Mon Sep 17 00:00:00 2001
From: Reilly Grant <reillyg@google.com>
Date: Wed, 25 Feb 2026 00:18:33 +0000
Subject: [PATCH] [M-146] Use std::ptrdiff_t for matrix dimensions and indices.
(Cherry-picked from 8a21714c60bdf0aa95405404e7322e455a34de72.)
PiperOrigin-RevId: 868743667
Bug: 482875307
Change-Id: I9de8e25ff4d22ff99bd63ad1573cfcc8d2aba990
---
diff --git a/ruy/mat.h b/ruy/mat.h
index c2254f9..520d717 100644
--- a/ruy/mat.h
+++ b/ruy/mat.h
@@ -328,7 +328,7 @@
}
inline std::ptrdiff_t FlatSize(const MatLayout& layout) {
- const int outerdim =
+ const std::ptrdiff_t outerdim =
layout.order == Order::kColMajor ? layout.cols : layout.rows;
return layout.stride * outerdim;
}
diff --git a/ruy/pack_avx2_fma.cc b/ruy/pack_avx2_fma.cc
index 2564b72..116854b 100644
--- a/ruy/pack_avx2_fma.cc
+++ b/ruy/pack_avx2_fma.cc
@@ -13,6 +13,7 @@
limitations under the License.
==============================================================================*/
+#include <cstddef>
#include <cstdint>
#include <cstring>
@@ -595,9 +596,9 @@
src_ptr, zerobuf, src_stride, remaining_src_cols, src_rows, packed_ptr,
trailing_buf);
- const int trailing_rows = src_rows & (kPackRows - 1);
+ const std::ptrdiff_t trailing_rows = src_rows & (kPackRows - 1);
if (trailing_rows > 0) {
- const int non_trailing_rows = src_rows & ~(kPackRows - 1);
+ const std::ptrdiff_t non_trailing_rows = src_rows & ~(kPackRows - 1);
memcpy(packed_ptr + kPackCols * non_trailing_rows, trailing_buf,
kPackCols * trailing_rows * sizeof(float));
}
Original Bug Report
webnn_graph_mojolpm_fuzzer: Segv on unknown address in ruy::MulFrontEndFromTrMulParams
Detailed Report: https://clusterfuzz.com/testcase?key=5384407544168448
Fuzzing Engine: libFuzzer Fuzz Target: webnn_graph_mojolpm_fuzzer Job Type: mac_libfuzzer_chrome_asan Platform Id: mac
Crash Type: Segv on unknown address Crash Address: Crash State: ruy::MulFrontEndFromTrMulParams void ruy::MulFrontEnd< tflite::cpu_backend_gemm::detail::GemmImplUsingRuy<float, float, float, float,
Sanitizer: address (ASAN)
Regressed: https://clusterfuzz.com/revisions?job=mac_libfuzzer_chrome_asan&range=1468407:1468456
Reproducer Testcase: https://clusterfuzz.com/download?testcase_id=5384407544168448
Issue filed automatically.
See https://chromium.googlesource.com/chromium/src/+/master/testing/libfuzzer/reproducing.md for instructions on reproducing this bug locally.