diff --git a/ios/web_view/test/web_view_back_forward_list_inttest.mm b/ios/web_view/test/web_view_back_forward_list_inttest.mm index 0000000000..1111111111 100644 --- a/ios/web_view/test/web_view_back_forward_list_inttest.mm +++ b/ios/web_view/test/web_view_back_forward_list_inttest.mm @@ -280,4 +280,43 @@ TEST_F(WebViewBackForwardListTest, TestCWVBackForwardListItemArrayForInLoop) { EXPECT_EQ(i, list.forwardList.count); } +// POC: heap-use-after-free when CWVBackForwardList outlives CWVWebView. +// -[CWVWebView dealloc] does not nil _backForwardList.navigationManager +// (unlike -shutDown), so a retained list dereferences a freed +// web::NavigationManagerImpl. +TEST_F(WebViewBackForwardListTest, UAF_BackForwardListOutlivesWebView) { + ASSERT_TRUE(test_server_->Start()); + GenerateTestPageUrls(); + + CWVBackForwardList* retained_list = nil; + // Use an autoreleasepool so the CWVWebView is deterministically deallocated + // when the last strong reference is dropped. + @autoreleasepool { + CWVWebView* popup = + [[CWVWebView alloc] initWithFrame:UIScreen.mainScreen.bounds + configuration:web_view_.configuration]; + ASSERT_TRUE(test::LoadUrl(popup, net::NSURLWithGURL(page1_url_))); + ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout( + base::test::ios::kWaitForPageLoadTimeout, ^{ + return [popup.title isEqualToString:@"page1"]; + })); + + // Embedder retains the public CWVBackForwardList object (e.g. for a + // "recently closed tabs" UI). This is a strong-returnable Obj-C object. + retained_list = popup.backForwardList; + ASSERT_NE(retained_list, nil); + + // Drop the only strong reference to the popup. -[CWVWebView dealloc] runs + // (NOT -shutDown), then ARC's .cxx_destruct destroys + // std::unique_ptr _webState, which frees + // NavigationManagerImpl. retained_list.navigationManager is now dangling. + popup = nil; + } + + // UAF: nil-guard at cwv_back_forward_list.mm:129 passes (pointer is + // dangling, not nil) -> virtual call GetLastCommittedItem() on freed object. + // Under ASAN: heap-use-after-free. + (void)retained_list.currentItem; +} + } // namespace ios_web_view