Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in V8
DescriptionInteger overflow in V8
ComponentV8
Bug ClassInteger Overflow
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker522126182
Fix commit7893ac23bc1e (v8/v8) +75/-8
CWECWE-472, CWE-190 (Integer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-06-10 (per vendor advisory)
Fix landed2026-06-12 (public git)
Disclosed2026-06-30
Patch gap18 days — fix public in source before the advisory

Background

`Array.prototype.join`
the builtin that concatenates array elements separated by a separator string, implemented in Torque in src/builtins/array-join.tq.
`nofSeparators` / `sepsLen`
the running count of separators to emit and the total separator byte-length separatorLength * nofSeparators, accumulated as intptr while sizing the result buffer.
`kStringMaxLength` / `kMaxFastArrayLength` / `kSmiMaxValue`
V8 limits: the maximum string length (~268MB/536MB), the maximum fast-array length (~32MB), and the maximum Smi value; fast-array length is guaranteed to fit in a Smi.
`TryInt32Mul`
an overflow-checked 32-bit multiply that branches to an IfOverflow label instead of wrapping.

Root Cause Analysis

The pre-patch separator-sizing code computed sepsLen = separatorLength * nofSeparatorsInt as an intptr and detected overflow with the division check if (sepsLen / separatorLength != nofSeparatorsInt) ThrowInvalidStringLength(context) inside the hot loop that adds separators. On 32-bit platforms the nofSeparators counter itself can overflow before that per-iteration check catches it, because the count is accumulated separately from the sepsLen product, so the total-length accounting can silently wrap and under-report the required buffer size.

The fix removes the in-loop division check entirely and instead validates once up front: in FastArrayJoin it bounds len <= kMaxFastArrayLength, observes that a separatorLength <= 1 fast array can never overflow, and for separatorLength > 1 && len > 1 computes TryInt32Mul(lenInt - 1, sepLenInt) and throws ThrowInvalidStringLength if it overflows or exceeds kStringMaxLength; the generic ArrayJoin path adds an equivalent early check that handles the Smi length case with TryInt32Mul (plus a 1-character-separator fast path) and immediately throws for a HeapNumber length because such lengths exceed kSmiMaxValue > kStringMaxLength. Because the total separator contribution is verified against kStringMaxLength before any buffer is sized or written, the nofSeparators/sepsLen computation can no longer overflow undetected. The commit states this “fixes a potential nofSeparators overflow on 32-bit platforms” in addition to removing a division from the hot loop.

Key insight
The single mistake was relying on a per-iteration division-based overflow check that ran after nofSeparators could already wrap on 32-bit builds; the fix hoists an overflow-checked TryInt32Mul bound against kStringMaxLength to the start of the join, before any counting or buffer sizing.

Attack Path

  1. Create a huge array Attacker JS allocates new Array(0xfffffffc), a length near 2^32 that, combined with a non-trivial separator, drives the separator-length accounting toward the 32-bit overflow boundary.
  2. Force the slow/counting path Setting a sparse element such as a[0] = "A".repeat(0x100000) pushes the join onto the loop that accumulates nofSeparators and sepsLen (regression test regress-522126182.js).
  3. Overflow the counter With separatorLength > 1 the pre-patch nofSeparators/sepsLen product overflows the 32-bit integer, and the in-loop division check fails to detect it, so totalStringLength is understated. (advisory: IntegerOverflow)
  4. Undersized allocation / OOB write The wrapped length is used to size the result buffer, so subsequent separator writes can exceed the allocation; the diff shows only the missing overflow check and the corrected buffer-length accounting, so the concrete out-of-bounds write is (inferred).

Impact Assessment

An attacker running JavaScript can call Array.prototype.join with a very large array and a multi-character separator so that separator-length accounting overflows a 32-bit integer, leading to an undersized result buffer and potential heap corruption in the renderer process; the precondition is only script execution plus a 32-bit V8 build (per the commit’s “on 32-bit platforms”). The advisory classifies the bug as IntegerOverflow / “Integer overflow in V8” and states no exploited-in-the-wild status; a memory-corruption or code-execution consequence beyond the length overflow shown is (inferred), since the patch itself demonstrates only the missing early overflow guard.

Changed Functions

FunctionChangeNotes
if
src/builtins/array-join.tq
modified
typeswitch
src/builtins/array-join.tq
modified

Files Changed

  • src/builtins/array-join.tq
  • test/mjsunit/regress/regress-522126182.js
  • test/mjsunit/regress/regress-crbug-897404.js

Audit Directions

  • Product-of-lengths sizing
    Audit other builtins that compute a result size as count * elementLength in intptr/int32 (repeat, padStart/padEnd, string concat, TypedArray fills) and verify overflow is checked before allocation, not per iteration.
  • 32-bit vs 64-bit width assumptions
    Look for size accounting that is safe on 64-bit intptr but can wrap on 32-bit platforms, especially counters accumulated separately from the product they bound.
  • Post-hoc division overflow checks
    Flag the a*b; if (a*b / b != a) idiom in hot loops; prefer hoisted TryInt32Mul/overflow-checked multiplication compared against kStringMaxLength before any buffer is created.
From 7893ac23bc1e640904892810891d144e4894ccd0 Mon Sep 17 00:00:00 2001
From: pthier <[email protected]>
Date: Fri, 12 Jun 2026 14:52:04 +0200
Subject: [PATCH] Check early for separator overflow in Array.prototype.join()

Instead of checking if separators overflow the maximum string length
each time we add a (or multiple) separator(s) to the buffer, we check
once in the beginning (based on the separator- and array-length).

Besides avoiding a division in the hot loop, this also fixes a potential
nofSeparators overflow on 32-bit platforms.

Fixed: 522126182
Change-Id: Iab05848b9ec1960a925e56b11bb74569438ebafb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7927166
Reviewed-by: Igor Sheludko <[email protected]>
Commit-Queue: Patrick Thier <[email protected]>
Cr-Commit-Position: refs/heads/main@{#107953}
---

diff --git a/src/builtins/array-join.tq b/src/builtins/array-join.tq
index dd1f45c..c9ec15e 100644
--- a/src/builtins/array-join.tq
+++ b/src/builtins/array-join.tq
@@ -206,11 +206,6 @@
 
     const nofSeparatorsInt: intptr = nofSeparators;
     const sepsLen: intptr = separatorLength * nofSeparatorsInt;
-    // Detect integer overflow
-    // TODO(turbofan): Replace with overflow-checked multiplication.
-    if (sepsLen / separatorLength != nofSeparatorsInt) deferred {
-        ThrowInvalidStringLength(context);
-      }
 
     this.totalStringLength = AddStringLength(this.totalStringLength, sepsLen);
     if (write) deferred {
@@ -357,11 +352,33 @@
 transitioning macro FastArrayJoin(
     implicit context: Context)(kind: constexpr ElementsKind,
     array: FastJSArrayForRead, sep: String, lengthNumber: Number): String {
-  const elements = array.elements;
-
+  // Check early if the separators might overflow.
   let len: uintptr = Convert<uintptr>(lengthNumber);
+  // Fast array length is guaranteed to fit in Smi.
+  dcheck(len <= Convert<uintptr>(kMaxFastArrayLength));
+  static_assert(Convert<uintptr>(kMaxFastArrayLength) < kSmiMaxValue);
+  // If separatorLength <= 1, fast arrays cannot overflow the separator counter
+  // or exceed kStringMaxLength from separators alone, because their maximum
+  // length is bounded by kMaxFastArrayLength (32MB) which is much smaller
+  // than kStringMaxLength (268MB/536MB). Thus we only need to perform checks
+  // if separatorLength > 1.
+  static_assert(
+      (Convert<uintptr>(kMaxFastArrayLength) - 1) <=
+      Convert<uintptr>(kStringMaxLength));
 
   const separatorLength: intptr = sep.length_intptr;
+  if (separatorLength > 1 && len > 1) {
+    const lenInt: int32 = Convert<int32>(Signed(len));
+    const sepLenInt: int32 = Convert<int32>(separatorLength);
+    try {
+      const res = TryInt32Mul(lenInt - 1, sepLenInt) otherwise IfOverflow;
+      if (res > Convert<int32>(kStringMaxLength)) goto IfOverflow;
+    } label IfOverflow {
+      ThrowInvalidStringLength(context);
+    }
+  }
+
+  const elements = array.elements;
   let nofSeparators: intptr = 0;
   let buffer: Buffer = NewBuffer(len, sep);
 
@@ -752,6 +769,38 @@
     implicit context: Context)(useToLocaleString: constexpr bool,
     o: JSReceiver, len: Number, sep: String, locales: JSAny,
     options: JSAny): JSAny {
+  // Check early if the separators might overflow.
+  const separatorLength: intptr = sep.length_intptr;
+  if (separatorLength >= 1) {
+    typeswitch (len) {
+      case (lenSmi: Smi): {
+        const lenInt: int32 = Convert<int32>(lenSmi);
+        if (lenInt > 1) {
+          try {
+            // Optimization for the most common case of 1-character separators
+            // (e.g. ',').  In this case, the total separator length is simply
+            // `len - 1`. We can bypass the multiplication entirely and perform
+            // a direct comparison on `len`.
+            if (separatorLength == 1 && len - 1 > kStringMaxLength) {
+              goto IfOverflow;
+            }
+            const sepLenInt: int32 = Convert<int32>(separatorLength);
+            const res = TryInt32Mul(lenInt - 1, sepLenInt) otherwise IfOverflow;
+            if (res > Convert<int32>(kStringMaxLength)) goto IfOverflow;
+          } label IfOverflow {
+            ThrowInvalidStringLength(context);
+          }
+        }
+      }
+      case (HeapNumber): {
+        // If len is a HeapNumber (and positive), it must be > kSmiMaxValue.
+        // Since kSmiMaxValue > kStringMaxLength, any join with a non-empty
+        // separator will inevitably exceed the maximum string length.
+        static_assert(kSmiMaxValue > Convert<uintptr>(kStringMaxLength));
+        ThrowInvalidStringLength(context);
+      }
+    }
+  }
   // If the receiver is not empty and not already being joined, continue with
   // the normal join algorithm.
   if (len > 0 && JoinStackPushInline(o)) {
@@ -829,6 +878,7 @@
   } label NotFastJSArray {
     // Fall through.
   }
+
   // TODO(ishell): perform stack checks rarely.
   PerformStackCheck(context);
   return CycleProtectedArrayJoin<JSArray>(
diff --git a/test/mjsunit/regress/regress-522126182.js b/test/mjsunit/regress/regress-522126182.js
new file mode 100644
index 0000000..777055f
--- /dev/null
+++ b/test/mjsunit/regress/regress-522126182.js
@@ -0,0 +1,17 @@
+// 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 TestLargeArrayJoinWithSeparator() {
+  let len = 0xfffffffc;
+  let a = new Array(len);
+  a[0] = "A".repeat(0x100000);  // Force slow path loop (dictionary elements)
+  assertThrows(() => a.join(","), RangeError);
+})();
+
+(function TestLargeArrayJoinEmptySeparator() {
+  let len = 0xfffffffc;
+  let a = new Array(len);
+  // Should not throw because separator is empty, and array has no elements.
+  assertEquals("", a.join(""));
+})();
diff --git a/test/mjsunit/regress/regress-crbug-897404.js b/test/mjsunit/regress/regress-crbug-897404.js
index 7e8b48d..3b11208 100644
--- a/test/mjsunit/regress/regress-crbug-897404.js
+++ b/test/mjsunit/regress/regress-crbug-897404.js
@@ -4,7 +4,7 @@
 
 function TestError() {}
 
-const a = new Array(2**32 - 1);
+const a = new Array(2**16 - 1);
 
 // Force early exit to avoid an unreasonably long test.
 a[0] = {
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/test/mjsunit/regress/regress-522126182.js b/test/mjsunit/regress/regress-522126182.js
new file mode 100644
index 0000000..777055f
--- /dev/null
+++ b/test/mjsunit/regress/regress-522126182.js
@@ -0,0 +1,17 @@
+// 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 TestLargeArrayJoinWithSeparator() {
+  let len = 0xfffffffc;
+  let a = new Array(len);
+  a[0] = "A".repeat(0x100000);  // Force slow path loop (dictionary elements)
+  assertThrows(() => a.join(","), RangeError);
+})();
+
+(function TestLargeArrayJoinEmptySeparator() {
+  let len = 0xfffffffc;
+  let a = new Array(len);
+  // Should not throw because separator is empty, and array has no elements.
+  assertEquals("", a.join(""));
+})();
diff --git a/test/mjsunit/regress/regress-crbug-897404.js b/test/mjsunit/regress/regress-crbug-897404.js
index 7e8b48d..3b11208 100644
--- a/test/mjsunit/regress/regress-crbug-897404.js
+++ b/test/mjsunit/regress/regress-crbug-897404.js
@@ -4,7 +4,7 @@
 
 function TestError() {}
 
-const a = new Array(2**32 - 1);
+const a = new Array(2**16 - 1);
 
 // Force early exit to avoid an unreasonably long test.
 a[0] = {
Loading diff…

Original Bug Report

reported by [email protected]

Array.prototype.join signed intptr wrap on 32-bit platforms leads to potential heap OOB write

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: On 32-bit V8 architectures (arm32/ia32), the signed intptr separator counter in Array.prototype.join can wrap negative when iterating over arrays with lengths approaching 2^32. Due to missing negative bounds checks in the Torque layer, the negative value is stored in the chunk list and interpreted by the C++ string flattening sink as a repeat-last count, resulting in a large linear heap OOB write.

Affected files:

  • src/builtins/array-join.tq
  • src/objects/objects.cc

Estimated timestamp from git blame: 2025-08-14

1. Summary of the Issue (Meant for Human Triage)

A highly severe, potential heap out-of-bounds (OOB) write vulnerability has been identified in V8’s Array.prototype.join implementation when executed on 32-bit architectures (arm32 and ia32).

In the Torque implementation ArrayJoinImpl, a signed intptr counter named nofSeparators is used to keep track of consecutive separators. When joining arrays with length properties approaching kMaxArrayLength (which is kMaxUInt32, or 0xFFFFFFFF), the loop can iterate billions of times. On 32-bit architectures, where an intptr is a signed 32-bit integer, this counter eventually overflows and wraps around to a negative value.

Because AddSeparators and AddStringLength lack lower-bounds checking or negativity guards, this negative counter value is processed as valid and stored as a negative Smi in the internal chunk list. When rendering the final string in the C++ routine WriteChunkListToFlat, the negative Smi is interpreted as a repeat_last directive. Since the bounds checks in the C++ memcpy loops are compiled out in production/release builds (being gated behind DCHECK macros), this results in a massive, linear heap buffer overflow (memcpy) of attacker-controlled data past a freshly allocated SeqOneByteString buffer.

Since 32-bit V8 architectures run with V8_ENABLE_SANDBOX=0 and lack pointer-compression cages or guard regions, this translates to a highly reliable and direct renderer heap corruption primitive. 64-bit systems are structurally unaffected because intptr is a 64-bit signed integer, meaning the counter cannot wrap negative.


2. Proof-of-Concept & Detailed Execution Flow

(Note: The steps below represent a potential trigger flow verified through static analysis and tooling; our agent tooling does not currently have the capability to execute this code dynamically.)

Step-by-Step Execution Flow Analysis

  1. Entry Point & Vector Initialization: An attacker invokes Array.prototype.join on a receiver with a length close to kMaxUInt32 (e.g., 0xFFFFFFFC) on a 32-bit platform:

    const L = 0x1000;
    const s = "A".repeat(L);
    Array.prototype.join.call({0: s, length: 0xFFFFFFFC}, ",");
    

    This invokes the GenericElementsAccessor or dictionary elements accessor fallback paths. At src/builtins/array-join.tq:450, the length is converted to a uintptr:

    const len: uintptr = Convert<uintptr>(lengthNumber);   // len = 0xFFFFFFFC
    
  2. Signed Counter Overflow: At src/builtins/array-join.tq:452, nofSeparators is initialized as a signed intptr (which is a 32-bit signed integer on 32-bit targets):

    let nofSeparators: intptr = 0;
    

    As the loop iterates over the len empty entries/holes, it bypasses adding string fragments but repeatedly increments the separator counter at src/builtins/array-join.tq:469:

    nofSeparators = nofSeparators + 1;
    

    Once nofSeparators exceeds 0x7FFFFFFF (2,147,483,647), it wraps around to negative values. For 0xFFFFFFFC iterations with empty elements, nofSeparators wraps to 0xFFFFFFFB, which represents -5 as a signed 32-bit integer.

  3. Bypassing AddSeparators and AddStringLength Guards: At the end of the loop, buffer.AddSeparators(nofSeparators, separatorLength, true) is called with nofSeparators = -5. Inside the macro AddSeparators (src/builtins/array-join.tq:202-220):

    if (nofSeparators == 0 || separatorLength == 0) return; // -5 != 0, bypasses
    const nofSeparatorsInt: intptr = nofSeparators;
    const sepsLen: intptr = separatorLength * nofSeparatorsInt; // 1 * -5 = -5
    if (sepsLen / separatorLength != nofSeparatorsInt) deferred { ... } // -5 / 1 == -5, bypasses
    

    The macro then calls AddStringLength with a negative length addition:

    this.totalStringLength = AddStringLength(this.totalStringLength, sepsLen); // totalStringLength decrements by 5
    

    AddStringLength (src/builtins/array-join.tq:142-150) uses TryIntPtrAdd and guards only against upper bounds exceeding kStringMaxLength, allowing negative values/decrements to slip through:

    const length: intptr = TryIntPtrAdd(lenA, lenB) otherwise IfOverflow;
    if (length > kStringMaxLength) goto IfOverflow; // No lower-bound check!
    return length;
    

    Finally, AddSeparators appends the negative value as a Smi into the chunk array:

    this.AppendToChunk(Convert<Smi>(nofSeparatorsInt)); // Stores Smi(-5)
    
  4. Buffer Allocation: BufferJoin in src/builtins/array-join.tq allocates a SeqOneByteString of size totalStringLength (which is L - 5 where L is the length of the string at index 0). The positivity check at line 325 (dcheck(IsValidPositiveSmi)) is a DCHECK only and is disabled in release builds.

  5. C++ Out-of-Bounds Sink (memcpy): The C++ helper WriteChunkListToFlat in src/objects/objects.cc processes the chunk list to fill the allocated sequential string. When it encounters the negative Smi element (value -5):

    int count;
    CHECK(Object::ToInt32(element, &count)); // count = -5
    if (count > 0) { ... }
    else {
      repeat_last = static_cast<uint32_t>(-count); // -(-5) = 5
      DCHECK(IsString(last_element)); // Bypassed in release builds
    }
    

    When repeat_last > 0 (which is now 5), the doubling-memcpy routine calculates the end destination:

    uint32_t string_length = Cast<String>(last_element)->length();
    uint32_t length_with_sep = string_length + separator_length;
    sinkchar* copy_end = sink + (length_with_sep * repeat_last) - separator_length;
    

    The loop duplicates the string data:

    while (sink < copy_end - copy_length) {
      DCHECK_LE(sink + copy_length, sink_end); // DCHECK ONLY (disabled in release)
      memcpy(sink, sink - copy_length, copy_length * sizeof(sinkchar));
      sink += copy_length;
      copy_length *= 2;
    }
    

    Because the destination buffer sink was allocated with length L - 5 but the logic copies 6L + 5 bytes, the loop writes 5L + 10 bytes beyond the allocated boundaries of the flat string, overwriting subsequent heap objects.

Suggested Fix

Either change nofSeparators to a uintptr type in ArrayJoinImpl (propagating the unsigned type to AddSeparators), or add a strict non-negative bounds check in AddSeparators:

if (nofSeparators <= 0 || separatorLength == 0) return;

3. Technical Verification Details (Automated Audit Logs - Reviewers may skip this section)

Prior Critic Verdict Verbatim

*   **Severity:** High (S1)
*   **Brief Notes / Reasoning:**
    The vulnerability report accurately identifies a layer-1 heap out-of-bounds (OOB) write in `Array.prototype.join` on 32-bit architectures (arm32/ia32). In `ArrayJoinImpl`, the `nofSeparators` counter is typed as an `intptr`. When joining a sparse or mostly empty array with a length approaching `kMaxArrayLength` (~4 billion elements), this signed 32-bit counter increments without overflow checks, eventually wrapping around to a negative value (e.g. -5). The `AddSeparators` and `AddStringLength` macros lack proper lower-bounds checking or negative guards for this counter, interpreting it as valid and eventually storing it as a negative Smi in the chunk list. In the C++ sink `WriteChunkListToFlat`, this negative Smi is interpreted as a `repeat_last` directive, which triggers an out-of-bounds `memcpy` write operation past the allocated bounds of the `SeqOneByteString`. 
    
    Since arm32 and ia32 architectures do not use pointer compression, the V8 sandbox and guard regions are absent (`V8_ENABLE_SANDBOX=0`). Therefore, this results in a direct renderer heap buffer overflow with attacker-controlled content and length, which is a powerful primitive for arbitrary code execution. The only constraint, ~4 billion iterations, is practically achievable in a few minutes due to the highly optimized, allocation-free `DictionaryElements` hole-path. The vulnerability maps to Class F (CSA/Torque builtin length arithmetic bug) and occurs on default flags on tier-1 architectures, fully justifying a High severity (S1).

Mitigations / Validators Checked (Environmental Assumptions)

  • V8 Sandbox / Guard Regions / Pointer Compression: On 32-bit targets (arm32 and ia32), V8_ENABLE_SANDBOX is 0, and there is no pointer-compression cage. Hence, there are no sandbox constraints preventing direct corruption of adjacent pointers or objects.
  • Codebase Investigator Checks:
    • Validated that TryIntPtrAdd in src/codegen/code-stub-assembler.cc:995-1002 checks for signed overflow, meaning a large positive lenA and small negative lenB bypasses IfOverflow because the mathematical sum fits safely within a signed 32-bit integer range.
    • Confirmed the loop condition and structure in ArrayJoinImpl skips adding string fragments for holes via if (str == kEmptyString) continue; (array-join.tq:484), allowing nofSeparators to continuously accumulate over billions of iterations allocation-free via DictionaryElements.
    • Confirmed WriteChunkListToFlat in src/objects/objects.cc:4396-4422 uses repeat_last in a while loop that calls memcpy(sink, sink - copy_length, ...) restricted exclusively by DCHECK_LE bounds checks, which are omitted in release builds.
  • AddSeparators Division Check (array-join.tq:211): The check sepsLen / separatorLength != nofSeparatorsInt performs signed division. Because -5 / 1 == -5, the division check is bypassed and fails to identify the negative value as an anomaly.
  • BufferJoin Positivity Verification (array-join.tq:325): The check dcheck(IsValidPositiveSmi(...)) is compiled out in release/non-debug builds.
  • WriteChunkListToFlat Bounds Checking (objects.cc:4312, 4380, 4408): All memory boundary verification utilizing sink_end is compiled out under #ifdef DEBUG, enabling unchecked sequential heap overwrite in release configurations.
  • 64-bit Systems: 64-bit systems are structurally unaffected because intptr is a 64-bit signed integer. Since the maximum loop length is capped at kMaxUInt32, the counter nofSeparators cannot overflow into a negative range on these systems.

Evaluated with Chrome root at commit: 71e7e1a98476f8faf0d126dc83935a84331b2194


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

Note: This bug has been automatically redirected to the top-level Chromium component. Please move it to the actual component: https://b.corp.google.com/components/1456800 once PoCs have been generated.

View on issue tracker