findStringMatches is only sent to the main frame's process.
Component: WebKit Site Isolation | b2c0cb3
Source/WebKit/UIProcess/WebPageProxy.cpp
Site isolation 환경에서 main frame의 process는 cross-origin iframe을 remote-frame proxy로만 인식합니다. 그래서 페이지 전체를 대상으로 하는 find 동작은 신뢰할 수 있는 UIProcess(WebPageProxy)가 조율해야 합니다. 한편 select, indicate, snapshot, replace 같은 후속 동작은 정수 index로 match를 지정합니다. 이제 UIProcess는 이 index를 (frame, process-local index) 쌍으로 변환해야 합니다. 문제는 이 쌍이 신뢰할 수 없는 WebContent process가 보고한 데이터로 구성된다는 점입니다.
WebPageProxy::findStringMatches는 이제 모든 WebContent process로 요청을 분산하도록 변경되었습니다. 각 process는 frame별로 묶은 match 목록을 반환합니다. 여기에 selection이 위치한 frame과 그 frame 안에서의 selection index(FindSelectionInFrame)가 함께 전달됩니다. UIProcess는 이 결과를 frame tree 순서대로 병합하고, maxMatchCount로 상한을 적용합니다. 이후 global index를 (frameID, local index)로 매핑하는 table을 유지하게 됩니다. selectFindMatch, indicateFindMatch, getImageForFindMatch, replaceMatches는 이 table을 기준으로 routing을 수행합니다. 또한 모든 결과가 도착한 뒤 find UI를 갱신하는 UpdateFindUIAfterFindingAllMatches IPC가 새로 추가되었습니다.
per-WCP reply { [(frameID, rects)], selectionInFrame{frameID, idx} }
└─► walk UI frame tree in order ─► cap at maxMatchCount
└─► m_findMatchLocations: globalIdx -> (frameID, indexInProcess)
follow-up: globalIdx ─► bounds-checked lookup ─► frameID
─► process resolved from UI frame tree at send time ─► send indexInProcess
Significance
기존에는 site isolation 환경에서 find-in-page가 cross-origin iframe 내부의 match를 찾지 못했습니다. 이제 UIProcess는 신뢰할 수 없는 WebContent process가 전달한 frame ID와 index를 바탕으로 process 간 routing 상태를 구성합니다. 다만 범위를 벗어난 selection index와 global index에는 이미 bounds check가 적용되어 있습니다(firstIndexAfterSelection, findMatchLocation).
Audit directions
- 응답에 섞인 소유하지 않은 frame ID.
didFindStringMatches는 보고된FrameIdentifier가 응답한 process에 실제로 속하는지 검사하지 않습니다. 게다가m_frameMatches.set/m_selectionIndices.set은 기존 값을 그대로 덮어씁니다. 그래서 compromised renderer는 자신이 소유하지 않은 frame의 entry를 추가하거나 교체할 수 있습니다. 또한 앞쪽 frame에 selection이 있다고 주장하는 방식으로firstIndexAfterSelection과 indicator 소유권을 조작할 수도 있습니다. - 보고 process와 수신 process의 불일치.
indexInProcess는 보고한 process에서 전달된 값입니다. 반면 후속 IPC는 UI frame tree상 해당 frame을 소유한 것으로 기록된 process로 전송됩니다. 두 process가 서로 다르면, 한 process 기준의 local index가 다른 process에 도달하게 됩니다. 이때 수신 측FindController는matchIndex >= m_findMatches.size()조건만 검사합니다. - Stale table.
collectMatchesInFrameTreeOrder는 UI tree에 없는 frame을 별도 처리 없이 누락시킵니다. 게다가 table은 aggregator가 소멸될 때에만 교체됩니다. find 요청이 겹치는 경우, process crash, find 도중의 navigation, process 종료로 응답이 유실되는 경우를 점검할 필요가 있습니다. 아울러 UI fan-out 결과 indicator가 0개 또는 2개로 남을 수 있는지도 확인해야 합니다. - WebContent 측 traversal.
Page::findTextMatches는treeOrder<ComposedTree>로result.ranges를 순회하면서firstRange()를 dereference합니다. 역방향 index 계산과 shadow tree에 걸친 range 처리를 살펴볼 필요가 있습니다. 한편FindController::findStringMatches에서는range.start.document().frame()에 대해frameID()를 호출합니다. 이 호출 앞에는 눈에 보이는 null check가 없으므로 함께 점검해야 합니다.