← All issues

[24] [grid layout] Don't call viewportContentsChanged() from scroll updates during render tree layout

Severity: Low | Component: WebCore rendering | bf80e3e

diff에서 isInRenderTreeLayout()이 true인 상태로 scrollTo()viewportContentsChanged()를 호출했음이 확인됩니다. debug 빌드에서는 assertion이 유발되고, release 빌드에서는 부분적으로 초기화된 grid track 상태가 읽힙니다. 손상된 상태는 memory-copy primitive가 아닌 layout 연산으로 흘러 들어가므로, 관찰 가능한 가장 현실적인 영향은 공격자가 조작한 HTML/CSS에 의한 renderer crash 재현입니다.

RenderLayerScrollableArea::scrollTo()LocalFrameView::viewportContentsChanged()를 호출합니다. 이 함수는 renderer geometry를 조회하여 visibility rect를 계산합니다. layout 도중(grid pre-layout 중 updateScrollInfoAfterLayout을 통한 경우 등) 호출되면 containing block이 아직 완료되지 않은 상태입니다. 이 상태에서 absolutely positioned iframe의 percentage padding을 아직 채워지지 않은 grid area에 대해 해석하려 하면 gridAreaRangeForOutOfFlow에서 assertion 실패가 발생합니다. 이 호출은 isInRenderTreeLayout()으로 보호되었습니다.

Source/WebCore/rendering/RenderLayerScrollableArea.cpp

if (scrollsOverflow())
view.frameView().didChangeScrollOffset();
 
- view.frameView().viewportContentsChanged();
+ if (!view.frameView().layoutContext().isInRenderTreeLayout())
+ view.frameView().viewportContentsChanged();

Layout-phase invariant 위반: layout 진행 중 발생한 scroll update에서 post-layout visibility-rect 조회가 호출되어, containing block이 layout을 완료하기 전에 renderer geometry를 읽는 패턴.

scrollTo() 안의 viewportContentsChanged() 호출에 !layoutContext().isInRenderTreeLayout() 조건이 추가되었습니다. 이 호출 지연은 안전합니다. performPostLayoutTasks()가 매 layout 완료 후 무조건적으로 viewportContentsChanged()를 이미 호출하고 있기 때문입니다. regression test에서는 percentage padding-left를 가진 absolutely positioned <iframe>을 CSS grid container 안에 중첩합니다. 이 container는 scaled descendant를 가진 vertical-rl 스크롤 가능한 자식을 포함합니다.

Render tree layout은 단계적으로 진행됩니다. engine은 top-down 방식으로 box geometry를 계산하며, 일부 helper는 geometry를 읽기 전에 layout이 완료된 상태를 전제로 합니다. LocalFrameView::layoutContext().isInRenderTreeLayout()은 layout pass 중에 true를 반환합니다. viewportContentsChanged()는 scroll 이후의 visibility를 계산하기 위해 renderer tree를 순회하는 함수로, 일반적으로 layout 완료 후 performPostLayoutTasks()에서 호출됩니다. CSS grid는 item 배치 전 pre-layout pass를 통해 track 크기를 결정합니다. out-of-flow grid item은 gridAreaRangeForOutOfFlow를 통해 containing block을 해석하며, 이 과정에서 track range를 읽습니다. percentage padding은 containing block의 inline size를 기준으로 해석됩니다.

패치 이전 scrollTo()viewportContentsChanged()를 무조건적으로 호출했습니다. grid pre-layout 중 updateScrollInfoAfterLayout을 통해 이 경로에 도달하면 containing block이 완료되지 않아 geometry가 부분적으로 구성된 상태입니다. 이 테스트 구성에서는 gridAreaRangeForOutOfFlow가 track이 채워지지 않은 grid area를 대상으로 percentage padding-left를 해석하려 합니다. 결과적으로 debug 빌드에서는 assertion이 유발되고, release 빌드에서는 초기화되지 않은 grid track 상태가 읽힙니다.

🔒

The phase-ordering invariant behind this layout assertion is examined, along with what the partially-constructed grid state could plausibly mean in release builds.

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

🔒

Multiple reusable audit patterns across scroll-update entry points and grid out-of-flow resolution, with concrete WebCore starting points for variant discovery.

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