← All issues

[3] Use-After-Free in BaseDateAndTimeInputType::didChangeValueFromControl

Severity: High | Component: WebCore HTML forms | 869d5c5

Rated High because the diff fixes a renderer-reachable UAF on a polymorphic C++ object with multiple virtual calls performed on this after the free; the regression test is a 20-line JS PoC that swaps input.type from an input event listener.

didChangeValueFromControl() dispatches the input event synchronously; an event handler that reassigns input.type destroys the current BaseDateAndTimeInputType instance (the unique owner is HTMLInputElement::m_inputType). When the handler returns, the method continues calling setupDateTimeChooserParameters() and showDateTimeChooser() on this — now freed memory.

Source/WebCore/html/BaseDateAndTimeInputType.cpp

bool BaseDateAndTimeInputType::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters)
{
- ASSERT(element());
+ RefPtr element = this->element();
+ if (!element)
+ return false;
- Ref element = *this->element();

Source/WebCore/html/BaseDateAndTimeInputType.h

+ void ref() const final { InputType::ref(); }
+ void deref() const final { InputType::deref(); }

Source/WebCore/html/shadow/DateTimeEditElement.cpp

void DateTimeEditElement::fieldValueChanged()
{
- if (m_editControlOwner)
- m_editControlOwner->didChangeValueFromControl();
+ if (RefPtr editControlOwner = m_editControlOwner)
+ editControlOwner->didChangeValueFromControl();
}

BaseDateAndTimeInputType is made ref-countable via ref()/deref() overrides forwarding to InputType; DateTimeEditElementEditControlOwner migrates from CanMakeWeakPtr to AbstractRefCountedAndCanMakeWeakPtr. Every m_editControlOwner dereference in DateTimeEditElement is rewritten to promote to a local RefPtr first. setupDateTimeChooserParameters swaps an ASSERT(element()) for a runtime null check so callers bail cleanly when the back-pointer has been cleared by a type swap.

Failure to retain a callback target across a JavaScript re-entrancy boundary, where the target's lifetime is owned by mutable HTML element state that the dispatched event can change.

<input type=date|datetime-local|month|time|week> renders its mm/dd/yyyy widget through a shadow tree built on DateTimeEditElement, which holds a back-pointer to its owning DateTimeEditElementEditControlOwner (implemented by BaseDateAndTimeInputType). On field edits, DateTimeEditElement::fieldValueChanged() notifies the owner via didChangeValueFromControl(), which fires the DOM input event. HTMLInputElement::m_inputType is the unique owner of the active InputType subclass; assigning input.type = 'text' causes HTMLInputElement::updateType() to replace m_inputType, destroying the previous instance. WeakPtr observes lifetime without extending it; RefPtr extends it; AbstractRefCountedAndCanMakeWeakPtr allows both simultaneously.

The shadow-tree control held its owner via a bare WeakPtr, so when the JS event handler reassigned input.type, no stack reference kept the BaseDateAndTimeInputType alive. Control unwound back into didChangeValueFromControl(), which proceeded with virtual calls on the freed this.

🔒

Detailed walkthrough of how a DOM event handler can free a C++ object mid-method, and the ownership and lifetime implications of the fix's structural change.

Subscribe to read more

🔒

Several reusable audit patterns identified, including a fuzzing harness shape that generalizes this bug class across multiple form control subsystems.

Subscribe to read more