Optional patch to make the race 100% deterministic for ASAN reproduction. Inserts a sleep between ReadFromFD's return and the use of `this`. This is NOT required for the bug to exist — the underlying race is real and the instrumentation test triggers it without this patch by holding the pipe writer open. This patch just removes any timing dependency on the speed of `delete this` for clean ASAN traces. Apply only when reproducing on Android with an ASAN WebView build. --- a/android_webview/js_sandbox/service/js_sandbox_isolate.cc +++ b/android_webview/js_sandbox/service/js_sandbox_isolate.cc @@ -38,6 +38,7 @@ #include "base/system/sys_info.h" #include "base/task/cancelable_task_tracker.h" #include "base/task/single_thread_task_runner.h" +#include "base/threading/platform_thread.h" #include "base/task/thread_pool.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/threading/thread_restrictions.h" @@ -525,9 +526,18 @@ void JsSandboxIsolate::ConvertPromiseToArrayBufferInThreadPool( std::unique_ptr> array_buffer, std::unique_ptr> resolver, void* inner_buffer) { - if (base::ReadFromFD(fd.get(), UNSAFE_TODO(base::span( + bool read_ok = base::ReadFromFD(fd.get(), UNSAFE_TODO(base::span( static_cast(inner_buffer), - base::checked_cast(length))))) { + base::checked_cast(length)))); + // POC instrumentation: read() unblocked. If close() was called while we + // were blocked, DeleteSelf is now in flight on the dedicated isolate + // thread. Give it time to run `delete this` before we touch + // control_task_runner_ below. With this sleep, ASAN catches the + // heap-use-after-free on every run. + LOG(ERROR) << "POC: ReadFromFD returned, sleeping before this-> access"; + base::PlatformThread::Sleep(base::Seconds(2)); + LOG(ERROR) << "POC: about to dereference this->control_task_runner_"; + if (read_ok) { control_task_runner_->PostTask( FROM_HERE, base::BindOnce(