← All reports

Validate `BackForwardGoToItem` against the dispatching connection

The check fired, and the process that failed it kept running.

Component: WebKit UIProcess IPC | 09efe9e

UIProcess는 sandbox 안에서 동작하는 WebContent process로부터 들어오는 IPC 메시지를 전부 신뢰하지 않습니다. 해당 process가 이미 장악된 상태일 수 있기 때문입니다. 이 전제 위에서 MESSAGE_CHECK / MESSAGE_CHECK_BASE 매크로가 프로토콜 불변식을 강제합니다. 검사에 실패하면 메시지를 보낸 Connection에 invalid 표시가 남고, 이후 Connection::dispatchMessage가 이 flag를 읽어 didReceiveInvalidMessage()를 호출하면서 문제가 된 process를 종료합니다. 한편 cross-site navigation에서는 process swap이 수행됩니다. 이때 commit된 process와 ProvisionalPageProxy가 새로 띄운 process가 잠시 함께 살아 있게 됩니다. 그래서 BackForwardGoToItem은 두 connection 중 어느 쪽으로 도착하더라도 정상적인 경우에 해당합니다.

패치 이전에는 검사 대상이 실제로 메시지를 전달한 IPC::Connection이 아니었습니다. 대신 WebPageProxy::legacyMainFrameProcess()를 기준으로 검사가 수행되었고, 이 commit이 그 부분을 바로잡습니다. 실제 Connection을 ProvisionalPageProxy와 WebBackForwardList를 거쳐 아래로 전달하도록 변경되었습니다. 덕분에 backForwardGoToItemShared()에서 해당 connection을 대상으로 MESSAGE_CHECK_BASE를 호출할 수 있습니다. 더 이상 쓰이지 않게 된 connectionForProcess() 우회 코드도 함께 제거되었습니다.

  Provisional process ──► BackForwardGoToItem ──► backForwardGoToItemShared()
                                                    │
  Before: MESSAGE_CHECK_BASE( ..., legacyMainFrameProcess()->connection() )
                                                    └──► invalid flag set on the
                                                         COMMITTED connection
                                                         (never dispatched again
                                                          for this message)
  After:  MESSAGE_CHECK_BASE( ..., connection )     └──► invalid flag on the
                                                         dispatching connection
                                                         ──► didReceiveInvalidMessage()

invalid-message flag가 엉뚱한 Connection 객체에 설정되었습니다. 그 결과 provisional process의 connection으로 도착한 BackForwardGoToItem 메시지에 대해서는, process 종료 경로가 아무런 흔적 없이 한 번도 동작하지 않았습니다. 엉뚱한 객체에 표시를 남기는 검증 매크로는 결국 fail open으로 동작합니다. 검사는 실행되고 실패 판정까지 나오지만, 그 뒤에 아무 일도 발생하지 않습니다.

찾아볼 만한 패턴은 분명합니다. MESSAGE_CHECK_BASE의 connection 인자가 메시지 자신의 dispatch 맥락이 아니라 page나 process 상태에서 복원되는 경우입니다. Narrow: 나머지 WebPageProxy와 WebBackForwardList 핸들러 가운데, 수신 지점에서 몇 프레임 아래에 있는 helper를 거쳐 MESSAGE_CHECK_BASE에 도달하는 경로를 점검할 필요가 있습니다. connection이 전달되지 않고 다시 유도되는 지점이 대체로 그런 곳이기 때문입니다. Wider: 더 일반적인 위험 구간은 process swap이 일어나는 window입니다. provisional load 도중 도달 가능한 UIProcess 핸들러는 살아 있는 connection 두 개를 두고 선택하게 됩니다. 이때 parameter로 받은 값이 아니라 관행에 따라 고르는 것이 곧 버그입니다. 출발 목록으로는 ProvisionalPageProxy의 메시지 표면이 자연스럽습니다. Widest: 검사 대상이 되는 연산과 별개로 식별된 객체를 변경하는 방식으로 실패를 보고하는 보안 통제는, 언제든 엉뚱한 객체에 대해 보고하도록 만들 수 있습니다. Code-review tell: MESSAGE_CHECK_BASE의 마지막 인자가 이미 scope 안에 들어와 있는 connection parameter가 아니라 someProcess()->connection() 같은 호출 표현식인 경우입니다.