High chrome Input Validation 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactImproper input validation in Date
DescriptionImproper input validation in Date
ComponentDate
Bug ClassInput Validation
Tracker565838017
Fix commit0e25d492d813 (chromium/src) +145/-25
CISA KEVNot listed
CreditedXinyang Ge (Anthropic), assisted by Claude
Disclosed2026-10-20

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
modified
for
third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
modified
if
third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc
modified
step_value_should_be
third_party/blink/renderer/core/html/forms/step_range.h
modified

Files Changed

  • third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/date-input-slot-assignment-crash.tentative.html
  • third_party/blink/renderer/core/html/forms/step_range.h
  • third_party/blink/renderer/core/html/forms/date_input_type.cc
  • third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
  • third_party/blink/renderer/platform/text/date_components.h
  • third_party/blink/renderer/core/html/forms/date_time_field_element.h
  • third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
  • third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
  • third_party/blink/renderer/core/html/forms/time_input_type.cc
  • third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
  • third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.h
  • third_party/blink/renderer/core/html/forms/month_input_type.cc
  • third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc
  • third_party/blink/renderer/core/html/forms/date_time_edit_element.h
From 0e25d492d813c083e39834cd47a7a39e7f253a71 Mon Sep 17 00:00:00 2001
From: David Baron <[email protected]>
Date: Tue, 29 Sep 2026 21:11:31 -0700
Subject: [PATCH] Don't rebuild date-time edit fields when layout parameters are unchanged.

The primary change here is changing DateTimeEditElement::GetLayout to
return early if the layout parameters are unchanged.  However, this
change has a number of prerequisites that are also included here:

* Adds default comparison operators to make it possible to compare
  LayoutParameters objects.

* Moves the needs-millisecond-field information into the
  LayoutParameters.

* Recompute style when changing values (in cases where, previously,
  elements were reconstructed, plus SetValueAsDateTimeFieldsState which
  was likely buggy) so that GetCustomStyleForLayoutObject will rerun
  when the value changes, since it depends on the value (e.g., setting a
  different width for 5-digit years).

* Clear cached type-ahead buffers when changing the values (which
  reconstructing the elements previously did, except in the
  SetValueAsDateTimeFieldsState case).

Fixed: 565838017
Change-Id: Ic248053b6731c83db92ba99e110a652acfb1ace0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8464610
Reviewed-by: Kent Tamura <[email protected]>
Commit-Queue: David Baron <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1707763}
---
diff --git a/third_party/blink/renderer/core/html/forms/date_input_type.cc b/third_party/blink/renderer/core/html/forms/date_input_type.cc
index 1ab0c2f..fc23eee 100644
--- a/third_party/blink/renderer/core/html/forms/date_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/date_input_type.cc
@@ -109,7 +109,7 @@
 void DateInputType::SetupLayoutParameters(
     DateTimeEditElement::LayoutParameters& layout_parameters,
     const DateComponents& date) const {
-  layout_parameters.date_time_format = layout_parameters.locale.DateFormat();
+  layout_parameters.date_time_format = layout_parameters.locale->DateFormat();
   layout_parameters.fallback_date_time_format = "yyyy-MM-dd";
   if (!ParseToDateComponents(
           GetElement().FastGetAttribute(html_names::kMinAttr),
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
index c7eb075..f13997b 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.cc
@@ -150,11 +150,7 @@
 }
 
 bool DateTimeEditBuilder::NeedMillisecondField() const {
-  static constexpr int kMillisecondsPerSecond =
-      static_cast<int>(base::Time::kMillisecondsPerSecond);
-  return date_value_.Millisecond() ||
-         !GetStepRange().Minimum().Remainder(kMillisecondsPerSecond).IsZero() ||
-         !GetStepRange().Step().Remainder(kMillisecondsPerSecond).IsZero();
+  return parameters_.need_millisecond_field;
 }
 
 void DateTimeEditBuilder::VisitField(date_time_format::FieldType field_type,
@@ -244,16 +240,16 @@
           field = MakeGarbageCollected<DateTimeSymbolicMonthFieldElement>(
               document, EditElement(),
               field_type == FieldType::kFieldTypeMonth
-                  ? parameters_.locale.ShortMonthLabels()
-                  : parameters_.locale.ShortStandAloneMonthLabels(),
+                  ? parameters_.locale->ShortMonthLabels()
+                  : parameters_.locale->ShortStandAloneMonthLabels(),
               min_month, max_month);
           break;
         case kCountForFullMonth:
           field = MakeGarbageCollected<DateTimeSymbolicMonthFieldElement>(
               document, EditElement(),
               field_type == FieldType::kFieldTypeMonth
-                  ? parameters_.locale.MonthLabels()
-                  : parameters_.locale.StandAloneMonthLabels(),
+                  ? parameters_.locale->MonthLabels()
+                  : parameters_.locale->StandAloneMonthLabels(),
               min_month, max_month);
           break;
         default:
@@ -276,7 +272,7 @@
     case FieldType::kFieldTypePeriodFlexible: {
       DateTimeFieldElement* field =
           MakeGarbageCollected<DateTimeAMPMFieldElement>(
-              document, EditElement(), parameters_.locale.TimeAmPmLabels());
+              document, EditElement(), parameters_.locale->TimeAmPmLabels());
       EditElement().AddField(field);
       return;
     }
@@ -290,7 +286,7 @@
       EditElement().AddField(field);
 
       if (NeedMillisecondField()) {
-        VisitLiteral(parameters_.locale.LocalizedDecimalSeparator());
+        VisitLiteral(parameters_.locale->LocalizedDecimalSeparator());
         VisitField(FieldType::kFieldTypeFractionalSecond, 3);
       }
       return;
@@ -454,7 +450,7 @@
   element->SetShadowPseudoId(text_pseudo_id);
   element->SetInlineStyleProperty(CSSPropertyID::kUnicodeBidi,
                                   CSSValueID::kNormal);
-  if (parameters_.locale.IsRtl() && text.length()) {
+  if (parameters_.locale->IsRtl() && text.length()) {
     unicode::CharDirection dir = unicode::Direction(text[0]);
     if (dir == unicode::kSegmentSeparator ||
         dir == unicode::kWhiteSpaceNeutral || dir == unicode::kOtherNeutral) {
@@ -727,6 +723,16 @@
 
 void DateTimeEditElement::GetLayout(const LayoutParameters& layout_parameters,
                                     const DateComponents& date_value) {
+  // Don't rebuild the layout if the parameters are unchanged.  This is not
+  // just an optimization since it prevents us from trying to do things to
+  // nodes at times when that is inappropriate, such as within
+  // SetIsInCanvasSubtree().
+  if (layout_parameters == current_layout_parameters_) {
+    CHECK(HasChildren());
+    return;
+  }
+  current_layout_parameters_.emplace(layout_parameters);
+
   // TODO(tkent): We assume this function never dispatches events. However this
   // can dispatch 'blur' event in Node::removeChild().
 
@@ -815,22 +821,40 @@
     const LayoutParameters& layout_parameters,
     const DateComponents& date) {
   GetLayout(layout_parameters, date);
-  for (const auto& field : fields_)
+  for (const auto& field : fields_) {
+    field->ResetTypeAhead();
     field->SetValueAsDate(date);
+  }
+  // Make CustomStyleForLayoutObject rerun, since it depends on the value.
+  SetNeedsStyleRecalc(
+      kSubtreeStyleChange,
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
 }
 
 void DateTimeEditElement::SetValueAsDateTimeFieldsState(
     const DateTimeFieldsState& date_time_fields_state) {
-  for (const auto& field : fields_)
+  for (const auto& field : fields_) {
+    field->ResetTypeAhead();
     field->SetValueAsDateTimeFieldsState(date_time_fields_state);
+  }
+  // Make CustomStyleForLayoutObject rerun, since it depends on the value.
+  SetNeedsStyleRecalc(
+      kSubtreeStyleChange,
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
 }
 
 void DateTimeEditElement::SetEmptyValue(
     const LayoutParameters& layout_parameters,
     const DateComponents& date_for_read_only_field) {
   GetLayout(layout_parameters, date_for_read_only_field);
-  for (const auto& field : fields_)
+  for (const auto& field : fields_) {
+    field->ResetTypeAhead();
     field->SetEmptyValue(DateTimeFieldElement::kDispatchNoEvent);
+  }
+  // Make CustomStyleForLayoutObject rerun, since it depends on the value.
+  SetNeedsStyleRecalc(
+      kSubtreeStyleChange,
+      StyleChangeReasonForTracing::Create(style_change_reason::kControlValue));
 }
 
 DateTimeFieldElement* DateTimeEditElement::GetField(DateTimeField type) const {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
index a1765e96..a2f82d1 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_edit_element.h
@@ -26,6 +26,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_DATE_TIME_EDIT_ELEMENT_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_DATE_TIME_EDIT_ELEMENT_H_
 
+#include <optional>
+
 #include "third_party/blink/public/mojom/input/focus_type.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
 #include "third_party/blink/renderer/core/html/forms/step_range.h"
@@ -63,21 +65,26 @@
   };
 
   struct LayoutParameters {
-    STACK_ALLOCATED();
+    DISALLOW_NEW();
 
    public:
     String date_time_format;
     String fallback_date_time_format;
-    Locale& locale;
+    // The Locale must be owned by the Document and come from
+    // Document::GetCachedLocale.
+    Locale* locale;
     const StepRange step_range;
     DateComponents minimum;
     DateComponents maximum;
     String placeholder_for_day;
     String placeholder_for_month;
     String placeholder_for_year;
+    bool need_millisecond_field = false;
 
     LayoutParameters(Locale& locale, const StepRange& step_range)
-        : locale(locale), step_range(step_range) {}
+        : locale(&locale), step_range(step_range) {}
+
+    bool operator==(const LayoutParameters&) const = default;
   };
 
   DateTimeEditElement(Document&, EditControlOwner&);
@@ -157,6 +164,7 @@
   void FieldDidChangeValueByKeyboard() override;
 
   HeapVector<Member<DateTimeFieldElement>, kMaximumNumberOfFields> fields_;
+  std::optional<LayoutParameters> current_layout_parameters_;
   Member<EditControlOwner> edit_control_owner_;
 };
 
diff --git a/third_party/blink/renderer/core/html/forms/date_time_field_element.h b/third_party/blink/renderer/core/html/forms/date_time_field_element.h
index 32437b31..03464fcd 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_field_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_field_element.h
@@ -83,6 +83,7 @@
   virtual String Placeholder() const = 0;
   virtual void PopulateDateTimeFieldsState(DateTimeFieldsState&) = 0;
   void RemoveEventHandler() { field_owner_ = nullptr; }
+  virtual void ResetTypeAhead() {}
   void SetDisabled();
   virtual void SetEmptyValue(EventBehavior = kDispatchNoEvent) = 0;
   virtual void SetValueAsDate(const DateComponents&) = 0;
diff --git a/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc b/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
index 6a1e4a40..43152b1 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_local_input_type.cc
@@ -165,17 +165,17 @@
     const DateComponents& date) const {
   if (ShouldHaveSecondField(date)) {
     layout_parameters.date_time_format =
-        layout_parameters.locale.DateTimeFormatWithSeconds();
+        layout_parameters.locale->DateTimeFormatWithSeconds();
     layout_parameters.fallback_date_time_format = "yyyy-MM-dd'T'HH:mm:ss";
   } else {
     layout_parameters.date_time_format =
-        layout_parameters.locale.DateTimeFormatWithoutSeconds();
+        layout_parameters.locale->DateTimeFormatWithoutSeconds();
     layout_parameters.fallback_date_time_format = "yyyy-MM-dd'T'HH:mm";
   }
 
   // Workaround for an Arabic date-time format issue.
   // TODO(crbug.com/40153320): Support ARABIC COMMA.
-  if (layout_parameters.locale.IsRtl()) {
+  if (layout_parameters.locale->IsRtl()) {
     layout_parameters.date_time_format =
         layout_parameters.date_time_format.RemoveCharacters(
             [](UChar ch) -> bool { return ch == uchar::kArabicComma; });
@@ -195,6 +195,16 @@
       GetLocale().QueryString(IDS_FORM_PLACEHOLDER_FOR_MONTH_FIELD);
   layout_parameters.placeholder_for_year =
       GetLocale().QueryString(IDS_FORM_PLACEHOLDER_FOR_YEAR_FIELD);
+  static constexpr int kMillisecondsPerSecond =
+      static_cast<int>(base::Time::kMillisecondsPerSecond);
+  layout_parameters.need_millisecond_field =
+      date.Millisecond() ||
+      !layout_parameters.step_range.Minimum()
+           .Remainder(kMillisecondsPerSecond)
+           .IsZero() ||
+      !layout_parameters.step_range.Step()
+           .Remainder(kMillisecondsPerSecond)
+           .IsZero();
 }
 
 bool DateTimeLocalInputType::IsValidFormat(bool has_year,
diff --git a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
index f3449a8..89453225 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.cc
@@ -178,6 +178,10 @@
   return placeholder_;
 }
 
+void DateTimeNumericFieldElement::ResetTypeAhead() {
+  type_ahead_buffer_.Clear();
+}
+
 void DateTimeNumericFieldElement::SetEmptyValue(EventBehavior event_behavior) {
   if (IsDisabled())
     return;
diff --git a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
index 20bb1350..a6a9cf5 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_numeric_field_element.h
@@ -91,6 +91,7 @@
   void Initialize(const AtomicString& pseudo, const String& ax_help_text);
   int Maximum() const;
   String Placeholder() const override;
+  void ResetTypeAhead() override;
   void SetEmptyValue(EventBehavior = kDispatchNoEvent) final;
   void SetValueAsInteger(int, EventBehavior = kDispatchNoEvent) override;
   int ValueAsInteger() const final;
diff --git a/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc b/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc
index b75f23f6..f91b6a00 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc
+++ b/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.cc
@@ -118,10 +118,25 @@
 void DateTimeSymbolicFieldElement::SetEmptyValue(EventBehavior event_behavior) {
   if (IsDisabled())
     return;
+  type_ahead_.ResetSession();
   selected_index_ = kInvalidIndex;
   UpdateVisibleValue(event_behavior);
 }
 
+void DateTimeSymbolicFieldElement::ResetTypeAhead() {
+  type_ahead_.ResetSession();
+}
+
+void DateTimeSymbolicFieldElement::SetFocused(
+    bool value,
+    mojom::blink::FocusType focus_type,
+    BlurEventBehavior blur_event_behavior) {
+  if (!value) {
+    type_ahead_.ResetSession();
+  }
+  DateTimeFieldElement::SetFocused(value, focus_type, blur_event_behavior);
+}
+
 void DateTimeSymbolicFieldElement::SetValueAsInteger(
     int new_selected_index,
     EventBehavior event_behavior) {
diff --git a/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.h b/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.h
index a133ff5..94fcaeae 100644
--- a/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.h
+++ b/third_party/blink/renderer/core/html/forms/date_time_symbolic_field_element.h
@@ -68,12 +68,16 @@
   void HandleKeyboardEvent(KeyboardEvent&) final;
   float MaximumWidth(const ComputedStyle&) override;
   String Placeholder() const override;
+  void ResetTypeAhead() override;
   void StepDown() final;
   void StepUp() final;
   String Value() const final;
   int ValueForARIAValueNow() const final;
   String VisibleValue() const final;
 
+  // Node functions.
+  void SetFocused(bool, mojom::blink::FocusType, BlurEventBehavior) override;
+
   // TypeAheadDataSource functions.
   int IndexOfSelectedOption() const override;
   int OptionCount() const override;
diff --git a/third_party/blink/renderer/core/html/forms/month_input_type.cc b/third_party/blink/renderer/core/html/forms/month_input_type.cc
index e8aa7c3..f9b943b0 100644
--- a/third_party/blink/renderer/core/html/forms/month_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/month_input_type.cc
@@ -143,7 +143,7 @@
 void MonthInputType::SetupLayoutParameters(
     DateTimeEditElement::LayoutParameters& layout_parameters,
     const DateComponents& date) const {
-  layout_parameters.date_time_format = layout_parameters.locale.MonthFormat();
+  layout_parameters.date_time_format = layout_parameters.locale->MonthFormat();
   layout_parameters.fallback_date_time_format = "yyyy-MM";
   if (!ParseToDateComponents(
           GetElement().FastGetAttribute(html_names::kMinAttr),
diff --git a/third_party/blink/renderer/core/html/forms/step_range.h b/third_party/blink/renderer/core/html/forms/step_range.h
index d388aaa..6e14164 100644
--- a/third_party/blink/renderer/core/html/forms/step_range.h
+++ b/third_party/blink/renderer/core/html/forms/step_range.h
@@ -65,6 +65,8 @@
           step_scale_factor(1),
           step_value_should_be(kStepValueShouldBeReal) {}
 
+    bool operator==(const StepDescription&) const = default;
+
     Decimal DefaultValue() const { return default_step * step_scale_factor; }
   };
 
@@ -79,6 +81,8 @@
             const Decimal& step,
             const StepDescription&);
 
+  bool operator==(const StepRange&) const = default;
+
   Decimal AlignValueForStep(const Decimal& current_value,
                             const Decimal& new_value) const;
   Decimal ClampValue(const Decimal& value) const;
diff --git a/third_party/blink/renderer/core/html/forms/time_input_type.cc b/third_party/blink/renderer/core/html/forms/time_input_type.cc
index 05744ab..551c518 100644
--- a/third_party/blink/renderer/core/html/forms/time_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/time_input_type.cc
@@ -143,11 +143,11 @@
     DateTimeEditElement::LayoutParameters& layout_parameters,
     const DateComponents& date) const {
   if (ShouldHaveSecondField(date)) {
-    layout_parameters.date_time_format = layout_parameters.locale.TimeFormat();
+    layout_parameters.date_time_format = layout_parameters.locale->TimeFormat();
     layout_parameters.fallback_date_time_format = "HH:mm:ss";
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/date-input-slot-assignment-crash.tentative.html b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/date-input-slot-assignment-crash.tentative.html
new file mode 100644
index 0000000..d7edfda
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/element/manual/draw-element-image/date-input-slot-assignment-crash.tentative.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Testcase for crash involving slot assignment and type=date input</title>
+<canvas layoutsubtree>
+  <div id="host1">
+    <template shadowrootmode=open>
+      <slot name=a></slot>
+    </template>
+    <div id=manuallyslotted slot=a>
+      <div id="host2">
+        <template shadowrootmode=open>
+          <slot></slot>
+        </template>
+        <input id=d type=date value="2001-02-03">
+        <span id=y></span>
+        <span id=z></span>
+      </div>
+    </div>
+  </div>
+</canvas>
+<script>
+  let slot1 = host1.shadowRoot.querySelector('slot');
+  let slot2 = host2.shadowRoot.querySelector('slot');
+  window.addEventListener("load", event => {
+    run(y);
+    host2.firstChild.remove();
+    slot2.assignedNodes();
+    run(z);
+  });
+  function run(element) {
+    d.focus();
+    manuallyslotted.slot = 'x';
+    slot1.assignedNodes();
+    element.slot = 'away';
+    manuallyslotted.slot = 'a';
+    slot1.assignedNodes();
+  }
+</script>
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.