[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.
이 diff는 해제된
this에 대해 virtual call이 여러 차례 수행되는 renderer-reachable UAF를 수정합니다. 대상은 다형 C++ 객체이며, regression test는inputevent 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
Source/WebCore/html/BaseDateAndTimeInputType.h
Source/WebCore/html/shadow/DateTimeEditElement.cpp
Patch Details
BaseDateAndTimeInputType은 ref()/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 상태에 의존합니다.
Background
<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은 두 가지를 동시에 지원합니다.
Analysis
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가 성립하지 않았습니다.
Audit directions
WeakPtr를 통해InputType으로 callback하는 shadow-tree control.HTMLInputElement::m_inputType은input.type = ...한 번으로 소멸될 수 있습니다. shadow element에 callback 소유자로 등록된 모든InputType서브클래스를 점검해야 합니다. 대상은SearchInputType,ColorInputType,RangeInputType,FileInputType과 해당did*FromControl진입점입니다. 각 callback 지점이 함수 전체 본문에 걸쳐InputType에 대한RefPtr을 보유하거나, re-entrant 호출 직후 즉시 반환하는지 확인해야 합니다.- JS re-entrancy 지점을 넘어 여러 차례 역참조되는
WeakPtr<Owner>. 새로운AbstractRefCountedAndCanMakeWeakPtr기반으로의 전환이 이번 수정의 표준 형태입니다. callback interface 역할을 하는 클래스에서CanMakeWeakPtr<를 검색하여,AbstractRefCountedAndCanMakeWeakPtr<>로 변경해야 하는 대상이 있는지 확인해야 합니다. - type swap 이후에도 도달 가능한
InputType의ASSERT(element()).Source/WebCore/html/을 검색하여,updateType()이 back-pointer를 null로 만든 이후에도 실행될 수 있는 caller가 있는지 추적해야 합니다. - Regression test를 audit 기준으로 활용. 각 input type에 대해,
input.type = <random>/input.remove()/input.replaceWith(...)를 수행하는 listener를 등록합니다. 이후 일반적인 사용자 동작 흐름, 즉 focus, 키 입력, picker 열기를 실행합니다. 이 형태의 harness는 과거에HTMLSelectElement와HTMLMediaElement에서 UAF를 발견한 바 있습니다.