High chrome Memory Corruption 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMemory corruption in Codecs
DescriptionMemory corruption in Codecs
ComponentCodecs
Bug ClassMemory Corruption
Tracker489489002
Fix commit5689f16923f5 (libyuv/libyuv) +145/-172
CISA KEVNot listed
CreditedCasper Woudenberg
Disclosed2026-09-08

Background

`libyuv`
Chrome’s YUV/ARGB image conversion and scaling library, used across the media and codec pipelines.
`asm volatile`
a GCC/Clang inline-assembly statement whose input, output, and clobber lists are the only contract the compiler honors about which registers it reads or destroys.
`pshufb`
an SSSE3 byte-shuffle instruction that selects destination bytes from a source register according to an index mask (here loaded from constants like kShuf0).
`ScaleRowDown34_SSSE3`
an SSSE3 down-scaler that reduces a row to 3/4 width using precomputed shuffle masks held in xmm3/xmm4/xmm5.

Root Cause Analysis

The affected SSSE3 scalers in scale_gcc.cc split their inline assembly into two or three separate asm volatile blocks: an initial block loaded constant masks (kShuf0, kShuf1, kShuf2, kMadd01, etc.) into xmm registers, and a later block executed the pixel loop assuming those xmm registers still held the loaded values. The invariant violated is that register state does not carry across disjoint asm volatile statements: the loader block never declared xmm3/xmm4/xmm5 as outputs or clobbers, so the compiler was free to reuse or overwrite those registers between blocks, leaving the loop’s pshufb/pmaddubsw operands undefined. With a corrupted shuffle-index mask, pshufb selects wrong source bytes and the routine writes malformed pixel data, and depending on register allocation and optimization level the scaling loop can operate on inconsistent state.

The fix consolidates each function’s stages into a single asm volatile block with named operand constraints and a complete clobber list, so the constants are loaded and consumed within one region the compiler treats atomically. Because there is now exactly one contiguous assembly statement, no compiler-inserted code or reallocation can clobber the mask registers between load and use.

Key insight
The core mistake was relying on xmm register contents to persist across separate asm volatile blocks without declaring them as outputs, inputs, or clobbers, which the compiler is never obligated to preserve. Merging the stages into one asm volatile block with named operands and a full clobber list makes the load-then-use dependency explicit and inseparable.

Attack Path

  1. Deliver crafted media A remote page or file feeds image/video content whose dimensions route decoding or resizing through one of the affected SSSE3 scaling paths.
  2. Trigger split-block execution The scaler runs the constant-loader asm block and then the pixel-loop asm block as two independent statements.
  3. Compiler clobbers mask registers Under the right build/optimization, the compiler reuses xmm3/xmm4/xmm5 (or another mask register) between the blocks because they were never declared, so the loop reads a stale or arbitrary shuffle mask.
  4. Corrupt shuffle/write pshufb with the wrong index mask, combined with the fixed pointer arithmetic, produces malformed output written to the destination buffer, corrupting memory contents processed downstream.

Impact Assessment

An attacker gains memory corruption in the media/codec processing path where libyuv scaling runs, typically a renderer or GPU/media utility process. Exploitation depends on the specific compiler and optimization decisions that actually clobber the undeclared mask registers, so the condition is build-dependent rather than universally reachable. The metadata classifies this as high-severity memory corruption in Codecs, without a published CVSS or exploit primitive to assert beyond the diff.

Files Changed

  • source/scale_gcc.cc

Audit Directions

  • Split `asm volatile` state carry
    Flag any function that loads values in one asm volatile block and consumes them in a later block, since register/flag state is not guaranteed to persist between disjoint statements.
  • Missing clobber/operand declarations
    Audit inline assembly whose used xmm/general registers are absent from the output, input, or clobber lists, as the compiler may reuse them freely.
  • Numbered vs. named operands
    Prefer converting positional (%0, %1) operands to named constraints across the codebase to reduce mis-wiring of constants and loop registers in multi-stage SIMD kernels.
From 5689f16923f516ba6eb5b5a3ee11dc90065a3641 Mon Sep 17 00:00:00 2001
From: Frank Barchard <fbarchard@google.com>
Date: Mon, 24 Aug 2026 16:10:25 -0700
Subject: [PATCH] Refactor multi-stage SSSE3 scalers into single asm blocks

Consolidate split asm volatile blocks into single blocks across SSSE3
scaling functions in scale_gcc.cc. This prevents potential register
corruption or clobbering by compiler optimizations between disjoint
asm blocks, and uses named operand constraints for clarity and safety.

List of affected functions in scale_gcc.cc:
- ScaleRowDown34_SSSE3: merge 2 asm blocks into 1
- ScaleRowDown34_1_Box_SSSE3: merge 3 asm blocks into 1
- ScaleRowDown34_0_Box_SSSE3: merge 3 asm blocks into 1
- ScaleRowDown38_2_Box_SSSE3: merge 2 asm blocks into 1
- ScaleRowDown38_3_Box_SSSE3: merge 2 asm blocks into 1
- ScaleARGBFilterCols_SSSE3: merge 2 asm blocks into 1

Test: ninja -C out/Release libyuv_unittest && ./out/Release/libyuv_unittest
Test: ninja -C out/Release_x86 libyuv_unittest && ./out/Release_x86/libyuv_unittest
Bug: 489489002
Change-Id: I900a590db2ef21b9f019a115699d3a270877ed28
TAG=agy
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/8280666
Reviewed-by: Wan-Teh Chang <wtc@google.com>
Commit-Queue: Frank Barchard <fbarchard@google.com>
---

diff --git a/source/scale_gcc.cc b/source/scale_gcc.cc
index e9100f6..b2a289e 100644
--- a/source/scale_gcc.cc
+++ b/source/scale_gcc.cc
@@ -471,34 +471,30 @@
                           int dst_width) {
   (void)src_stride;
   asm volatile(
-      "movdqa      %0,%%xmm3                     \n"
-      "movdqa      %1,%%xmm4                     \n"
-      "movdqa      %2,%%xmm5                     \n"
-      :
-      : "m"(kShuf0),  // %0
-        "m"(kShuf1),  // %1
-        "m"(kShuf2)   // %2
-  );
-  asm volatile(
-      "1:          \n"
-      "movdqu      (%0),%%xmm0                   \n"
-      "movdqu      0x10(%0),%%xmm2               \n"
-      "lea         0x20(%0),%0                   \n"
+      "movdqa      %[kShuf0],%%xmm3              \n"
+      "movdqa      %[kShuf1],%%xmm4              \n"
+      "movdqa      %[kShuf2],%%xmm5              \n"
+      "1:                                        \n"
+      "movdqu      (%[src_ptr]),%%xmm0           \n"
+      "movdqu      0x10(%[src_ptr]),%%xmm2       \n"
+      "lea         0x20(%[src_ptr]),%[src_ptr]   \n"
       "movdqa      %%xmm2,%%xmm1                 \n"
       "palignr     $0x8,%%xmm0,%%xmm1            \n"
       "pshufb      %%xmm3,%%xmm0                 \n"
       "pshufb      %%xmm4,%%xmm1                 \n"
       "pshufb      %%xmm5,%%xmm2                 \n"
-      "movq        %%xmm0,(%1)                   \n"
-      "movq        %%xmm1,0x8(%1)                \n"
-      "movq        %%xmm2,0x10(%1)               \n"
-      "lea         0x18(%1),%1                   \n"
-      "sub         $0x18,%2                      \n"
+      "movq        %%xmm0,(%[dst_ptr])           \n"
+      "movq        %%xmm1,0x8(%[dst_ptr])        \n"
+      "movq        %%xmm2,0x10(%[dst_ptr])       \n"
+      "lea         0x18(%[dst_ptr]),%[dst_ptr]   \n"
+      "sub         $0x18,%[dst_width]            \n"
       "jg          1b                            \n"
-      : "+r"(src_ptr),   // %0
-        "+r"(dst_ptr),   // %1
-        "+r"(dst_width)  // %2
-      :
+      : [src_ptr] "+r"(src_ptr),
+        [dst_ptr] "+r"(dst_ptr),
+        [dst_width] "+r"(dst_width)
+      : [kShuf0] "m"(kShuf0),
+        [kShuf1] "m"(kShuf1),
+        [kShuf2] "m"(kShuf2)
       : "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5");
 }
 
@@ -507,61 +503,55 @@
                                 uint8_t* dst_ptr,
                                 int dst_width) {
   asm volatile(
-      "movdqa      %0,%%xmm2                     \n"  // kShuf01
-      "movdqa      %1,%%xmm3                     \n"  // kShuf11
-      "movdqa      %2,%%xmm4                     \n"  // kShuf21
-      :
-      : "m"(kShuf01),  // %0
-        "m"(kShuf11),  // %1
-        "m"(kShuf21)   // %2
-  );
-  asm volatile(
-      "movdqa      %0,%%xmm5                     \n"  // kMadd01
-      "movdqa      %1,%%xmm0                     \n"  // kMadd11
-      "movdqa      %2,%%xmm1                     \n"  // kRound34
-      :
-      : "m"(kMadd01),  // %0
-        "m"(kMadd11),  // %1
-        "m"(kRound34)  // %2
-  );
-  asm volatile(
-      "1:          \n"
-      "movdqu      (%0),%%xmm6                   \n"
-      "movdqu      0x00(%0,%3,1),%%xmm7          \n"
+      "movdqa      %[kShuf01],%%xmm2             \n"
+      "movdqa      %[kShuf11],%%xmm3             \n"
+      "movdqa      %[kShuf21],%%xmm4             \n"
+      "movdqa      %[kMadd01],%%xmm5             \n"
+      "movdqa      %[kMadd11],%%xmm0             \n"
+      "movdqa      %[kRound34],%%xmm1            \n"
+      "1:                                        \n"
+      "movdqu      (%[src_ptr]),%%xmm6           \n"
+      "movdqu      0x00(%[src_ptr],%[src_stride],1),%%xmm7 \n"
       "pavgb       %%xmm7,%%xmm6                 \n"
       "pshufb      %%xmm2,%%xmm6                 \n"
       "pmaddubsw   %%xmm5,%%xmm6                 \n"
       "paddsw      %%xmm1,%%xmm6                 \n"
       "psrlw       $0x2,%%xmm6                   \n"
       "packuswb    %%xmm6,%%xmm6                 \n"
-      "movq        %%xmm6,(%1)                   \n"
-      "movdqu      0x8(%0),%%xmm6                \n"
-      "movdqu      0x8(%0,%3,1),%%xmm7           \n"
+      "movq        %%xmm6,(%[dst_ptr])           \n"
+      "movdqu      0x8(%[src_ptr]),%%xmm6        \n"
+      "movdqu      0x8(%[src_ptr],%[src_stride],1),%%xmm7 \n"
       "pavgb       %%xmm7,%%xmm6                 \n"
       "pshufb      %%xmm3,%%xmm6                 \n"
       "pmaddubsw   %%xmm0,%%xmm6                 \n"
       "paddsw      %%xmm1,%%xmm6                 \n"
       "psrlw       $0x2,%%xmm6                   \n"
       "packuswb    %%xmm6,%%xmm6                 \n"
-      "movq        %%xmm6,0x8(%1)                \n"
-      "movdqu      0x10(%0),%%xmm6               \n"
-      "movdqu      0x10(%0,%3,1),%%xmm7          \n"
-      "lea         0x20(%0),%0                   \n"
+      "movq        %%xmm6,0x8(%[dst_ptr])        \n"
+      "movdqu      0x10(%[src_ptr]),%%xmm6       \n"
+      "movdqu      0x10(%[src_ptr],%[src_stride],1),%%xmm7 \n"
+      "lea         0x20(%[src_ptr]),%[src_ptr]   \n"
       "pavgb       %%xmm7,%%xmm6                 \n"
       "pshufb      %%xmm4,%%xmm6                 \n"
-      "pmaddubsw   %4,%%xmm6                     \n"
+      "pmaddubsw   %[kMadd21],%%xmm6             \n"
       "paddsw      %%xmm1,%%xmm6                 \n"
       "psrlw       $0x2,%%xmm6                   \n"
       "packuswb    %%xmm6,%%xmm6                 \n"
-      "movq        %%xmm6,0x10(%1)               \n"
-      "lea         0x18(%1),%1                   \n"
-      "sub         $0x18,%2                      \n"
+      "movq        %%xmm6,0x10(%[dst_ptr])       \n"
+      "lea         0x18(%[dst_ptr]),%[dst_ptr]   \n"
+      "sub         $0x18,%[dst_width]            \n"
       "jg          1b                            \n"
-      : "+r"(src_ptr),    // %0
-        "+r"(dst_ptr),    // %1
-        "+r"(dst_width)   // %2
-      : "r"(src_stride),  // %3
-        "m"(kMadd21)      // %4
+      : [src_ptr] "+r"(src_ptr),
+        [dst_ptr] "+r"(dst_ptr),
+        [dst_width] "+r"(dst_width)
+      : [src_stride] "r"(src_stride),
+        [kShuf01] "m"(kShuf01),
+        [kShuf11] "m"(kShuf11),
+        [kShuf21] "m"(kShuf21),
+        [kMadd01] "m"(kMadd01),
+        [kMadd11] "m"(kMadd11),
+        [kRound34] "m"(kRound34),
+        [kMadd21] "m"(kMadd21)
       : "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6",
         "xmm7");
 }
@@ -571,28 +561,15 @@
                                 uint8_t* dst_ptr,
                                 int dst_width) {
   asm volatile(
-      "movdqa      %0,%%xmm2                     \n"  // kShuf01
-      "movdqa      %1,%%xmm3                     \n"  // kShuf11
-      "movdqa      %2,%%xmm4                     \n"  // kShuf21
-      :
-      : "m"(kShuf01),  // %0
-        "m"(kShuf11),  // %1
-        "m"(kShuf21)   // %2
-  );
-  asm volatile(
-      "movdqa      %0,%%xmm5                     \n"  // kMadd01
-      "movdqa      %1,%%xmm0                     \n"  // kMadd11
-      "movdqa      %2,%%xmm1                     \n"  // kRound34
-      :
-      : "m"(kMadd01),  // %0
-        "m"(kMadd11),  // %1
-        "m"(kRound34)  // %2
-  );
-
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.