← All issues

Site Isolation: Cross-Site iframe history.back() Support

e12ff02

Source/WebCore/loader/NavigationScheduler.cpp

-ScheduledHistoryNavigation::ScheduledHistoryNavigation(int historySteps)
- : ScheduledNavigation(0, lockHistory, lockBackForwardList, false, true)
- , m_historySteps(historySteps)
- , m_historyItem(page.backForward().itemAtIndex(historySteps))
-{ }
+ScheduledHistoryNavigation::ScheduledHistoryNavigation(int historySteps)
+ : ScheduledNavigation(0, lockHistory, lockBackForwardList, false, true)
+ , m_historySteps(historySteps)
+{ /* HistoryItem lookup deferred; lazy via targetHistoryItem() */ }
 
void ScheduledHistoryNavigation::fire(LocalFrame& frame)
{
+ if (frame.settings().useUIProcessForBackForwardItemLoading()) {
+ frame.loader().client().dispatchGoToBackForwardItemAtIndex(m_historySteps);
+ return;
+ }
auto* historyItem = targetHistoryItem();
...
}

Source/WebKit/UIProcess/WebPageProxy.cpp

+void WebPageProxy::goToBackForwardItemAtIndex(int32_t index, FrameIdentifier frameID)
+{
+ auto* item = m_backForwardList->itemAtIndex(index);
+ if (!item)
+ return;
+ updateFrameIdentifierForBackForwardItem(*item, frameID);
+ goToBackForwardItem(*item);
+}

Source/WebCore/loader/FrameLoader.cpp

+void FrameLoader::setPendingAsyncBackForwardNavigation()
+{
+ m_asyncBackForwardNavigationState = AsyncBackForwardNavigationState::Pending;
+}
+void FrameLoader::cancelPendingAsyncBackForwardNavigation()
+{
+ if (m_asyncBackForwardNavigationState == AsyncBackForwardNavigationState::Pending)
+ m_asyncBackForwardNavigationState = AsyncBackForwardNavigationState::Cancelled;
+}

WebKit의 Site Isolation 모델에서 cross-site iframe은 별도의 WebProcess 인스턴스에서 실행됩니다. 권위 있는 back-forward list(WebBackForwardList)는 UIProcess가 관리합니다. 기존 구조에서는 WebProcess 내 NavigationScheduler가 스케줄 시점에 sync IPC를 통해 HistoryItem을 즉시 조회한 뒤, page->goToItem()을 직접 호출하는 방식이었습니다. 이 경로는 multi-process navigation에서 per-frame 상태를 올바르게 처리하는 UIProcess 주도 경로를 우회하게 됩니다. 결과적으로 Site Isolation 환경에서 cross-site iframe의 history.back()history.go(n)이 정상적으로 동작하지 않았습니다.

이 수정은 HistoryItem 조회를 지연(lazy) 방식으로 전환합니다. WebProcess는 새로 추가된 비동기 IPC 메시지 GoToBackForwardItemAtIndex를 통해 step count만 UIProcess에 전달합니다. UIProcess가 올바른 HistoryItem과 child frame 상태를 직접 조회한 뒤 navigation을 시작하는 구조입니다. 또한 FrameLoader에 tri-state 비동기 navigation 상태 머신(None → Pending → Cancelled)이 추가되어 traversal 생명주기를 추적합니다. 한 가지 미묘한 복잡성이 존재합니다. spec을 준수하는 same-document navigation(fragment 변경, pushState)은 진행 중인 traversal을 취소해서는 안 됩니다. 이를 위해 새 back-forward entry가 삽입되는 시점에 pending step count를 조정하는 처리가 추가되었습니다.

Before (broken for cross-site frames):
  history.back() → sync IPC resolveHistoryItem(step) → page->goToItem()

After (new async path):
  history.back() → async IPC GoToBackForwardItemAtIndex(step)
                      └─► UIProcess: itemAtIndex(step) → goToBackForwardItem()

FrameLoader state:
  [None] ──► setPending() ──► [Pending] ──► fragment/new load ──► [Cancelled]

이 변경은 cross-process back-forward navigation 구조에서 중요한 전환점에 해당합니다. 스케줄링 경로에서 sync IPC를 제거했다는 점이 핵심입니다. 새로운 비동기 모델은 per-frame 상태를 올바르게 처리하지만, navigation 스케줄링과 back-forward list가 교차하는 영역에 복잡성을 추가합니다. 이 영역은 correctness와 security 모두에서 역사적으로 민감한 부분입니다.

🔒

이 취약점 패턴의 변종을 찾기 위한 구체적인 탐색 방향이 포함되어 있습니다

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