[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를 읽는 패턴.
Patch Details
scrollTo() 안의 viewportContentsChanged() 호출에 !layoutContext().isInRenderTreeLayout() 조건이 추가되었습니다. 이 호출 지연은 안전합니다. performPostLayoutTasks()가 매 layout 완료 후 무조건적으로 viewportContentsChanged()를 이미 호출하고 있기 때문입니다. regression test에서는 percentage padding-left를 가진 absolutely positioned <iframe>을 CSS grid container 안에 중첩합니다. 이 container는 scaled descendant를 가진 vertical-rl 스크롤 가능한 자식을 포함합니다.
Background
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를 기준으로 해석됩니다.
Analysis
패치 이전 scrollTo()는 viewportContentsChanged()를 무조건적으로 호출했습니다. grid pre-layout 중 updateScrollInfoAfterLayout을 통해 이 경로에 도달하면 containing block이 완료되지 않아 geometry가 부분적으로 구성된 상태입니다. 이 테스트 구성에서는 gridAreaRangeForOutOfFlow가 track이 채워지지 않은 grid area를 대상으로 percentage padding-left를 해석하려 합니다. 결과적으로 debug 빌드에서는 assertion이 유발되고, release 빌드에서는 초기화되지 않은 grid track 상태가 읽힙니다.
Aaaa Aaaaaaaaaaa Aaa a Aaaaa Aaaaaaaaa Aaaaa Aaaaaaaaa Aa Aaaa Aaaaaaaaaa a Aaaaaa Aaaaaaa Aaaa Aaa Aaa Aaaaa Aaaa Aaaaaaaaaaaaaaaa Aaaaaaa Aaaaaa Aaaaaaaaaaa Aa Aaaaaaaaaaaaa Aaaaaaaaaaaaaaa Aaaa Aaa Aaa Aaaaaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaa Aaa Aaaa Aaaaaaaaaa Aaaaaaaa Aa Aaaaaaaaaa Aaaaaaaaaa Aaaa Aaaaa Aaa Aaaa Aaaaaa Aa Aaa Aaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa a Aaa Aaaaaaaa Aaaaaaaaa Aaaaa Aaaaa Aaaa Aa Aaaa Aaaaa Aaaa Aaaa
Aaaaaaa Aaaa Aa Aaaaa Aa Aaa Aaa Aaaaaaaaaa Aaaaaaa Aaa Aaaa Aaaa Aaaaa Aaaaa Aaa Aaaaa Aa Aaaaaa Aa Aa Aaaa Aaaaaa Aaaa Aaaa Aa Aaa Aaaaaaaa Aaa Aaaaa Aaaaa Aaaaaaaaaa Aaaa Aaaaa Aaaaa Aa Aaa Aaa Aaa a Aa Aaaa Aa Aaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaa Aaaaaa Aaa Aaaaaa Aaa Aaa Aa Aaaaaaa Aaaaa Aaaa Aa Aaa Aaaaaa
a Aaaa Aaaaa Aaaaaaaaa Aa Aaaaaaaa Aaaaaaaa Aaaa Aaaaa Aaaaaa
🔒The phase-ordering invariant behind this layout assertion is examined, along with what the partially-constructed grid state could plausibly mean in release builds.
더 확인하려면 구독해 주세요
Audit directions
a Aaaaaaaa Aaaa Aaaaaa Aa Aaaa Aaaaaa Aaaaaa Aaaa Aaaa Aaaaaaaaaaa Aaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaaa Aa Aa Aaa Aa Aaaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaaaa Aa Aaaaaaaaaa a Aaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaaaa Aaa Aaaaa Aaaaaaaa Aaaaaaaa Aaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaa Aaaaaa
a Aaaaaaaa Aaaa Aa Aaaa Aaaaa Aaa Aa Aaaa Aaaaaaaaaaa Aaaa Aaaaaaa Aaaaaaaaaa a Aa Aaa Aaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaaa a Aa Aaa Aa Aaa Aaaaaa Aa Aaaaaaaaaa Aaaaaaaaaa Aaaa Aaaaa Aaaaaaaaaaaaaaaaaaa Aaa Aaaaaa
a Aaaaaaaaa Aaaaaa Aa Aaaaaa Aaaaaaa Aaaaa Aaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aa Aaaaaaa Aaa Aaaaaaaaa Aaaaaaaa Aaaa Aaa Aaaaaa Aaaaaaaaa Aaaaaa Aa Aaa a Aa Aa Aaaaaa Aaa Aaaaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aaaaaa Aaaaaa Aaaa Aaaaaa
a Aaaaa a Aaa Aaa Aaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa Aaa Aaaa Aaaaaaaa Aaaaaaaa Aaa Aaaaa Aaaaa Aaaaa Aaaaaaaaaaaaaaa Aa Aaaaaaaa Aa Aaa Aaaa Aa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aaaaaa
🔒Multiple reusable audit patterns across scroll-update entry points and grid out-of-flow resolution, with concrete WebCore starting points for variant discovery.
더 확인하려면 구독해 주세요