diff --git a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc --- a/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc +++ b/third_party/blink/renderer/core/fragment_directive/text_fragment_handler.cc @@ -4,7 +4,10 @@ #include "third_party/blink/renderer/core/fragment_directive/text_fragment_handler.h" +#include "base/functional/bind.h" #include "base/metrics/histogram_functions.h" +#include "base/task/sequenced_task_runner.h" +#include "base/task/thread_pool.h" #include "base/strings/strcat.h" #include "components/shared_highlighting/core/common/disabled_sites.h" #include "components/shared_highlighting/core/common/fragment_directives_utils.h" @@ -25,6 +28,7 @@ #include "third_party/blink/renderer/core/fragment_directive/text_fragment_selector_generator.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/frame/location.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/frame/visual_viewport.h" #include "third_party/blink/renderer/core/input/event_handler.h" @@ -61,6 +65,43 @@ void TextFragmentHandler::Cancel() { } void TextFragmentHandler::RequestSelector(RequestSelectorCallback callback) { + // --------------------------------------------------------------------------- + // POC: simulate a compromised renderer for the Android LinkToTextCoordinator + // TYPED-only-cancel TOCTOU. + // + // When the browser's LinkToTextCoordinator binds TextFragmentReceiver and + // calls RequestSelector(), a real attacker would: + // (1) initiate a renderer-initiated cross-origin navigation (transition = + // LINK, so onDidStartNavigationInPrimaryMainFrame early-returns), then + // (2) reply on this pipe AFTER the browser has swapped + // GetPrimaryMainFrame() to the victim RFH but BEFORE the 100 ms + // timeout fires. + // + // The reply pipe outlives this LocalFrame's detach (the Java-side mProducer + // is only closed in LinkToTextCoordinator.cleanup()), so a compromised + // renderer can write the reply from raw process memory after detach. To keep + // this patch minimal we approximate that by posting the reply on a + // sequenced task runner that is not tied to the frame lifetime. + // --------------------------------------------------------------------------- + if (GetFrame() && GetFrame()->IsOutermostMainFrame() && + GetFrame()->DomWindow()) { + // (1) Renderer-initiated navigation -> PageTransition::LINK on the Java + // side, so the cancel guard at LinkToTextCoordinator.java:261 is + // skipped. The victim was prerendered via