← 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

이 diff는 해제된 this에 대해 virtual call이 여러 차례 수행되는 renderer-reachable UAF를 수정합니다. 대상은 다형 C++ 객체이며, regression test는 input event listener에서 input.type을 교체하는 20줄짜리 JS PoC입니다.

didChangeValueFromControl()input event를 동기적으로 dispatch합니다. 이때 event handler가 input.type을 재할당하면, 현재의 BaseDateAndTimeInputType 인스턴스가 소멸됩니다. 해당 인스턴스의 유일한 소유자는 HTMLInputElement::m_inputType입니다. handler가 반환된 뒤, 메서드는 이미 해제된 this에 대해 setupDateTimeChooserParameters()showDateTimeChooser() 호출을 계속 수행합니다.

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();
}

BaseDateAndTimeInputTyperef()/deref() override를 통해 ref-countable 구조로 전환되었으며, 두 메서드는 모두 InputType에 위임합니다. DateTimeEditElementEditControlOwner의 기반은 CanMakeWeakPtr에서 AbstractRefCountedAndCanMakeWeakPtr로 변경되었고, DateTimeEditElement 내의 모든 m_editControlOwner 역참조는 로컬 RefPtr로 먼저 promote하는 방식으로 재작성되었습니다. setupDateTimeChooserParameters에서는 ASSERT(element())가 런타임 null check로 교체되었습니다. type swap으로 back-pointer가 초기화된 경우, 호출자가 정상적으로 처리를 중단하게 됩니다.

JavaScript re-entrancy 경계를 넘어 callback 대상을 유지하지 않은 패턴. 대상의 lifetime은 dispatch된 event가 변경할 수 있는 가변 HTML element 상태에 의존합니다.

<input type=date|datetime-local|month|time|week>DateTimeEditElement를 기반으로 구성된 shadow tree를 통해 mm/dd/yyyy 위젯을 렌더링합니다. DateTimeEditElement는 소유자인 DateTimeEditElementEditControlOwner를 가리키는 back-pointer를 보유하며, 실제 구현체는 BaseDateAndTimeInputType입니다. 필드 값이 수정되면 DateTimeEditElement::fieldValueChanged()가 호출됩니다. 이 함수는 didChangeValueFromControl()을 통해 소유자에게 알림을 전달하고, 결과적으로 DOM input event가 발생합니다. HTMLInputElement::m_inputType은 활성 InputType 서브클래스의 유일한 소유자입니다. input.type = 'text'를 할당하면 HTMLInputElement::updateType()m_inputType을 교체하며, 이전 인스턴스는 소멸됩니다. WeakPtr은 lifetime을 연장하지 않고 관찰만 하는 반면, RefPtr은 lifetime을 연장합니다. AbstractRefCountedAndCanMakeWeakPtr은 두 가지를 동시에 지원합니다.

shadow-tree control은 소유자를 bare WeakPtr로만 보유하고 있었습니다. 따라서 JS event handler가 input.type을 재할당하면, BaseDateAndTimeInputType을 살려두는 stack 참조가 존재하지 않게 됩니다. 실행 흐름이 didChangeValueFromControl()로 돌아왔을 때, 이미 해제된 this에 대해 virtual call이 계속 수행되었습니다.

이를 실제 exploit으로 발전시키려면, event handler 실행 중에 해제된 슬롯에 동일한 크기의 allocation을 경합해 넣는 방법을 고려할 수 있습니다. 대상은 다른 InputType 서브클래스이거나, 크기가 유사한 scriptable 객체가 될 수 있습니다. 이후 해제된 객체에 대한 vtable dispatch를 악용하거나, setupDateTimeChooserParameters / showDateTimeChooser 내의 멤버 read를 노리는 경로가 이론적으로 가능합니다. 해제된 this에 대한 virtual dispatch는 heap reclaim에 성공했을 때 vtable 기반 control-flow hijack으로 이어지는 전형적인 전조 조건에 해당합니다. heap reclaim이 이루어지지 않은 경우, 관찰되는 영향은 renderer crash입니다.

이 vulnerability는 WebContent renderer 내부의 memory safety를 약화시킵니다. HTML 명세는 InputType이 자신이 발생시킨 event의 dispatch 과정에서 살아있음을 암묵적으로 전제합니다. 수정 이전에는 m_inputType이 handler 내부에서 교체될 경우 이 invariant가 성립하지 않았습니다.