← All issues

[3] Highlight API iterator-invalidation UAF in clearFromSetLike

Severity: High | Component: CSS Custom Highlight API | 13d284a

이 취약점이 High로 평가된 이유는, document teardown 과정에서 실제 use-after-free가 발생한 것이 ASAN crash(Highlight::clearFromSetLike)로 확인되었기 때문입니다. repaintRange()를 통한 re-entrant 수정 경로는 WebKit의 synchronous layout 모델을 고려했을 때 이론적으로 가능합니다. 다만 controlled read/write primitive로의 확장은 re-entrant window 동안 heap reclamation이 필요한데, 아직 검증되지 않았지만 아키텍처상으로는 가능성이 존재합니다.

이 수정은 이전 embargoed patch에 대한 후속 fix입니다. clearFromSetLike()는 이제 순회를 시작하기 전에 std::exchange를 통해 m_highlightRanges의 내용을 stack-local 임시 변수로 옮깁니다. 따라서 repaintRange() callback 도중 멤버에 re-entrant 수정이 발생하더라도, 이미 비워진 컨테이너에 대한 동작이 됩니다.

Source/WebCore/Modules/highlight/Highlight.cpp

void Highlight::clearFromSetLike()
{
- for (auto& highlightRange : m_highlightRanges)
+ for (auto& highlightRange : std::exchange(m_highlightRanges, { }))
repaintRange(highlightRange->range());
m_highlightRanges.clear();
}

이 변경은 단 한 줄로 이루어집니다. 순회 대상을 m_highlightRanges 대신 std::exchange(m_highlightRanges, { })로 교체했습니다. 이 표현식은 vector의 내용을 임시 변수로 원자적으로 이동시키고, 원래 멤버를 빈 상태로 만듭니다. loop body인 repaintRange(highlightRange->range())는 변경되지 않았습니다. 마지막 줄의 m_highlightRanges.clear()는 이제 no-op이 되었지만(멤버가 이미 비어 있으므로), 방어적 코드로 남아 있습니다.

CSS Custom Highlight API는 JavaScript에서 AbstractRange를 포함하는 Highlight 객체를 생성할 수 있게 해 주며, 이 객체들은 document의 HighlightRegistry에 등록됩니다. highlight가 지워질 때(clearFromSetLike), 각 range는 시각적 강조 표시를 제거하기 위해 repaint되어야 합니다. repaintRange()는 해당 range와 교차하는 DOM 노드를 탐색하며 각각에 대해 renderer->repaint()를 호출합니다. 이 과정에서 WebKit의 synchronous layout 모델에 따라 style 재계산과 layout이 동기적으로 유발될 수 있습니다.

std::exchange(obj, {})는 단일 표현식에서 obj의 값을 꺼내고 기본 생성된 빈 값으로 교체한 뒤 원래 값을 반환하는 C++ idiom입니다. WebKit에서 이 drain-before-iterate 패턴은 re-entrant container 수정에 대한 방어적 관용구로 널리 확립되어 있습니다.

Callback을 호출하는 순회 loop 도중 live container에 re-entrant 수정이 발생해 iterator가 무효화된 패턴.

패치 이전에는 clearFromSetLike()가 각 요소에 repaintRange()를 호출하면서 live m_highlightRanges 멤버를 직접 순회했습니다. repaintRange()는 교차하는 노드를 탐색하며 renderer->repaint()를 호출하는데, 이 과정에서 style 재계산, layout, 기타 repaint 부작용이 동기적으로 유발될 수 있습니다.

이런 repaint callback은 m_highlightRanges를 re-entrant하게 수정할 수 있습니다. 예를 들어 observer callback이나 highlight set을 변경하는 다른 코드 경로를 통해 range가 추가되거나 제거되는 상황이 가능합니다. 이것이 re-entrancy boundary에 해당합니다. native code가 layout/style 처리 체계를 호출하면, 그 처리가 반환되기 전에 동기적으로 다시 진입해 객체 상태를 변경할 수 있는 지점입니다.

이런 re-entrant 수정이 발생하면 순회 도중 iterator가 무효화됩니다. 결과적으로 vector의 backing store에 대한 use-after-free 또는 out-of-bounds access로 이어집니다. ASAN crash 시그니처(Highlight::clearFromSetLike; HighlightRegistry::clear; Document::commonTeardown)를 통해 이 경로가 document teardown 중에 실제로 도달 가능함이 확인되었습니다.

🔒

The ownership model and escalation potential of this iterator-invalidation bug are explored in depth

더 확인하려면 구독해 주세요

🔒

Multiple reusable audit patterns identified, with concrete starting points for variant discovery across WebCore

더 확인하려면 구독해 주세요