← All issues

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

An input handler that reassigns the field's own type frees the InputType object mid-method, then dispatch continues on the freed this.

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.

To weaponize, an attacker would race a same-sized allocation into the freed slot during the event handler — another InputType subclass or any similarly-sized scriptable object — then exploit the vtable dispatch on the freed object or one of the member reads in setupDateTimeChooserParameters / showDateTimeChooser. The presence of virtual dispatch on freed this is the classic vtable-based control-flow hijack precursor on successful heap reclaim; absent reclaim, the observable effect is a renderer crash.

This vulnerability weakens memory safety inside the WebContent renderer. The HTML spec implicitly assumes an InputType survives dispatch of the events it triggers; before the fix, that invariant did not hold whenever m_inputType was replaced from inside such a handler.