Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Input
DescriptionInappropriate implementation in Input
ComponentInput
Bug ClassLogic Error
Tracker522404101
Fix commit19dbe763d928 (chromium/src) +42/-3
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
components/input/web_input_event_builders_android.cc
modified
TEST
components/input/web_input_event_builders_android_unittest.cc
modified

Files Changed

  • components/input/web_input_event_builders_android.cc
  • components/input/web_input_event_builders_android_unittest.cc
From 19dbe763d92833bd30bfd96e3cfcf8c4058fe16b Mon Sep 17 00:00:00 2001
From: Tzarial <[email protected]>
Date: Thu, 25 Jun 2026 08:38:02 -0700
Subject: [PATCH] [agy][input] Fix key encoding

On Android, physical hardware keyboards delivering supplementary-plane
characters (e.g. emojis >= U+10000) are truncated in the Build() path
of WebKeyboardEventBuilder because of an implicit narrowing cast from
int to char16_t when assigning to result.unmodified_text[0].

This causes a divergence where JS event.key is correct (since dom_key
retains the full 21-bit scalar), but the text[] field receives only
the low 16 bits. Since the password-reuse detector (PhishGuard)
uses text[] for comparison, it fails to match saved credentials,
enabling a phishing bypass.

This CL fixes the issue by encoding supplementary-plane characters
as UTF-16 surrogate pairs into unmodified_text and copying the whole
array to text.

Test: components_unittests
Change-Id: I2317dba06c61614423b26bf806881d4fff5f9509
Fixed: 522404101
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7994938
Commit-Queue: Tzarial <[email protected]>
Reviewed-by: Aman Verma <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1652453}
---

diff --git a/components/input/web_input_event_builders_android.cc b/components/input/web_input_event_builders_android.cc
index 4c4264c6..7cbd1ac 100644
--- a/components/input/web_input_event_builders_android.cc
+++ b/components/input/web_input_event_builders_android.cc
@@ -7,6 +7,8 @@
 #include <android/input.h>
 
 #include "base/check.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversion_utils.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/events/android/key_event_utils.h"
@@ -144,14 +146,19 @@
   result.dom_code = static_cast<int>(dom_code);
   result.dom_key = GetDomKeyFromEvent(env, android_key_event, keycode,
                                       modifiers, unicode_character);
-  result.unmodified_text[0] = unicode_character;
+  std::u16string unmodified_text_str;
+  if (unicode_character) {
+    base::WriteUnicodeCharacter(unicode_character, &unmodified_text_str);
+  }
   if (result.windows_key_code == ui::VKEY_RETURN) {
     // This is the same behavior as GTK:
     // We need to treat the enter key as a key press of character \r. This
     // is apparently just how webkit handles it and what it expects.
-    result.unmodified_text[0] = '\r';
+    unmodified_text_str = u"\r";
   }
-  result.text[0] = result.unmodified_text[0];
+  base::u16cstrlcpy(result.unmodified_text.data(), unmodified_text_str.c_str(),
+                    result.unmodified_text.size());
+  result.text = result.unmodified_text;
   result.is_system_key = is_system_key;
   result.is_confirmed_physical_keyboard_input =
       IsConfirmedPhysicalKeyboardEvent(env, android_key_event);
diff --git a/components/input/web_input_event_builders_android_unittest.cc b/components/input/web_input_event_builders_android_unittest.cc
index cd9fdce..ca6abc2 100644
--- a/components/input/web_input_event_builders_android_unittest.cc
+++ b/components/input/web_input_event_builders_android_unittest.cc
@@ -177,6 +177,38 @@
   EXPECT_EQ(ui::DomKey::UNIDENTIFIED, ui::DomKey(web_event.dom_key));
 }
 
+// Verifies that supplementary-plane code points (e.g. emojis or extra-plane
+// chars) are encoded as surrogate pairs in unmodified_text and text, matching
+// dom_key and avoiding truncation / narrowing.
+TEST(WebInputEventBuilderAndroidTest, SupplementaryPlaneTextVsDomKeyMatch) {
+  constexpr int kCodePoint = 0x1F600;  // U+1F600 GRINNING FACE
+
+  // Use the synthetic-event form (env=nullptr) so the test doesn't depend on a
+  // device KCM; |unicode_character| is supplied directly, mirroring what
+  // ImeAdapterImpl.sendKeyEvent forwards from KeyEvent.getUnicodeChar().
+  WebKeyboardEvent web_event = input::WebKeyboardEventBuilder::Build(
+      /*env=*/nullptr, /*android_key_event=*/nullptr,
+      WebKeyboardEvent::Type::kKeyDown, /*modifiers=*/0,
+      blink::WebInputEvent::GetStaticTimeStampForTests(),
+      /*keycode=*/AKEYCODE_A, /*scancode=*/0,
+      /*unicode_character=*/kCodePoint,
+      /*is_system_key=*/false);
+
+  // dom_key preserves the full 21-bit scalar -> JS event.key will be "😀".
+  EXPECT_EQ(ui::DomKey::FromCharacter(kCodePoint),
+            ui::DomKey(web_event.dom_key))
+      << "dom_key must hold the full code point";
+  std::string js_event_key =
+      ui::KeycodeConverter::DomKeyToKeyString(ui::DomKey(web_event.dom_key));
+  EXPECT_EQ("\xF0\x9F\x98\x80", js_event_key);  // UTF-8 of U+1F600
+
+  // text[] now holds the properly encoded UTF-16 surrogate pair.
+  std::u16string text(web_event.text.data());
+  EXPECT_EQ(2u, text.size()) << "text[] must hold the 2 surrogate code units";
+  EXPECT_EQ(std::u16string(u"\U0001F600"), text)
+      << "text[] must match the saved-password encoding (surrogate pair)";
+}
+
 // Testing new Android keycode introduced in API 24.
 TEST(WebInputEventBuilderAndroidTest, CutCopyPasteKey) {
   JNIEnv* env = AttachCurrentThread();
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/input/web_input_event_builders_android_unittest.cc b/components/input/web_input_event_builders_android_unittest.cc
index cd9fdce..ca6abc2 100644
--- a/components/input/web_input_event_builders_android_unittest.cc
+++ b/components/input/web_input_event_builders_android_unittest.cc
@@ -177,6 +177,38 @@
   EXPECT_EQ(ui::DomKey::UNIDENTIFIED, ui::DomKey(web_event.dom_key));
 }
 
+// Verifies that supplementary-plane code points (e.g. emojis or extra-plane
+// chars) are encoded as surrogate pairs in unmodified_text and text, matching
+// dom_key and avoiding truncation / narrowing.
+TEST(WebInputEventBuilderAndroidTest, SupplementaryPlaneTextVsDomKeyMatch) {
+  constexpr int kCodePoint = 0x1F600;  // U+1F600 GRINNING FACE
+
+  // Use the synthetic-event form (env=nullptr) so the test doesn't depend on a
+  // device KCM; |unicode_character| is supplied directly, mirroring what
+  // ImeAdapterImpl.sendKeyEvent forwards from KeyEvent.getUnicodeChar().
+  WebKeyboardEvent web_event = input::WebKeyboardEventBuilder::Build(
+      /*env=*/nullptr, /*android_key_event=*/nullptr,
+      WebKeyboardEvent::Type::kKeyDown, /*modifiers=*/0,
+      blink::WebInputEvent::GetStaticTimeStampForTests(),
+      /*keycode=*/AKEYCODE_A, /*scancode=*/0,
+      /*unicode_character=*/kCodePoint,
+      /*is_system_key=*/false);
+
+  // dom_key preserves the full 21-bit scalar -> JS event.key will be "😀".
+  EXPECT_EQ(ui::DomKey::FromCharacter(kCodePoint),
+            ui::DomKey(web_event.dom_key))
+      << "dom_key must hold the full code point";
+  std::string js_event_key =
+      ui::KeycodeConverter::DomKeyToKeyString(ui::DomKey(web_event.dom_key));
+  EXPECT_EQ("\xF0\x9F\x98\x80", js_event_key);  // UTF-8 of U+1F600
+
+  // text[] now holds the properly encoded UTF-16 surrogate pair.
+  std::u16string text(web_event.text.data());
+  EXPECT_EQ(2u, text.size()) << "text[] must hold the 2 surrogate code units";
+  EXPECT_EQ(std::u16string(u"\U0001F600"), text)
+      << "text[] must match the saved-password encoding (surrogate pair)";
+}
+
 // Testing new Android keycode introduced in API 24.
 TEST(WebInputEventBuilderAndroidTest, CutCopyPasteKey) {
   JNIEnv* env = AttachCurrentThread();
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.