diff --git a/src/heap/factory-base.cc b/src/heap/factory-base.cc index 2eb959798ff..6901cb5d511 100644 --- a/src/heap/factory-base.cc +++ b/src/heap/factory-base.cc @@ -33,10 +33,26 @@ #include "src/objects/template-objects-inl.h" #include "src/roots/roots.h" #include "src/sandbox/check.h" +// === POC INJECTION: includes === +#include +#include "src/base/sanitizer/asan.h" +// === POC INJECTION END === namespace v8 { namespace internal { +// ========================================================================= +// POC INJECTION: thread-local hooks to (a) force NewJSDispatchHandle into +// its GC slow-path -- simulating JSDispatchTable exhaustion, the real-world +// trigger -- and (b) ASAN-poison the stale interior pointer captured by +// HeapObject::AllocateAndInstallJSDispatchHandle overload #2 so that the +// unmodified Release_Store at objects-inl.h:1369 trips ASAN. +// ========================================================================= +thread_local void* g_poc_stale_location = nullptr; +thread_local Address g_poc_host_before = 0; +thread_local Address* g_poc_host_slot = nullptr; +// ========================================================================= + template template Handle FactoryBase::NewHeapNumber() { @@ -1498,8 +1514,15 @@ JSDispatchHandle FactoryBase::NewJSDispatchHandle( uint16_t parameter_count, DirectHandle code, JSDispatchTable::Space* space) { JSDispatchTable& jdt = isolate()->js_dispatch_table(); - auto result = - jdt.TryAllocateAndInitializeEntry(space, parameter_count, *code); + // === POC INJECTION: simulate JSDispatchTable exhaustion ================== + // When armed (g_poc_stale_location != nullptr), skip the fast path so that + // RetryCustomAllocateOrFail below runs mark-compact GC -- exactly what + // happens in production when the table fills or a segment commit fails. + std::optional result; + if (g_poc_stale_location == nullptr) { + result = jdt.TryAllocateAndInitializeEntry(space, parameter_count, *code); + } + // === POC INJECTION END =================================================== if (result) { return *result; } @@ -1513,6 +1536,37 @@ JSDispatchHandle FactoryBase::NewJSDispatchHandle( auto allocator = isolate()->heap()->allocator(); allocator->RetryCustomAllocateOrFail( allocate_callback, type, GarbageCollectionReason::kAllocationFailure); + // === POC INJECTION: detect evacuation and poison the stale pointer ======= + // RetryCustomAllocateOrFail just ran at least one mark-compact GC. With + // --stress-compaction, the FeedbackCell that |g_poc_stale_location| points + // INTO has been evacuated. Its DirectHandle slot (|*g_poc_host_slot|) was + // updated by the GC, but the raw |location| parameter held by the caller in + // objects-inl.h overload #2 was NOT. Poison the stale region so the + // unmodified `base::AsAtomic32::Release_Store(location, handle)` at + // objects-inl.h:1369 -- which is about to execute on return -- trips ASAN. + if (g_poc_stale_location && g_poc_host_slot) { + Address after = *g_poc_host_slot; + if (after != g_poc_host_before) { + fprintf( + stderr, + "[POC] FeedbackCell EVACUATED by mark-compact inside " + "NewJSDispatchHandle slow path:\n" + "[POC] before GC: 0x%lx\n" + "[POC] after GC : 0x%lx\n" + "[POC] stale interior 'location' (overload #2 param) = %p\n" + "[POC] Poisoning stale region. The UNMODIFIED Release_Store at\n" + "[POC] objects-inl.h:1369 is about to write 4 bytes here.\n", + static_cast(g_poc_host_before), + static_cast(after), g_poc_stale_location); + ASAN_POISON_MEMORY_REGION(g_poc_stale_location, sizeof(JSDispatchHandle)); + } else { + fprintf(stderr, + "[POC] FeedbackCell did NOT move (0x%lx) -- evacuation aborted " + "this round; retry.\n", + static_cast(after)); + } + } + // === POC INJECTION END =================================================== return *result; } diff --git a/src/objects/feedback-vector.cc b/src/objects/feedback-vector.cc index 8af792bb2f0..8c7d46827d7 100644 --- a/src/objects/feedback-vector.cc +++ b/src/objects/feedback-vector.cc @@ -23,6 +23,12 @@ namespace v8::internal { +// === POC INJECTION: hooks defined in factory-base.cc ===================== +extern thread_local void* g_poc_stale_location; +extern thread_local Address g_poc_host_before; +extern thread_local Address* g_poc_host_slot; +// ========================================================================= + FeedbackSlot FeedbackVectorSpec::AddSlot(FeedbackSlotKind kind) { int slot = slot_count(); int entries_per_slot = FeedbackMetadata::GetSlotSize(kind); @@ -235,8 +241,25 @@ DirectHandle ClosureFeedbackCellArray::New( uint16_t parameter_count = shared->feedback_metadata()->GetCreateClosureParameterCount(i); auto initial_code = BUILTIN_CODE(isolate, CompileLazy); + // === POC INJECTION: arm slow-path simulation ========================= + // Capture an indirect handle slot so factory-base.cc can re-read the + // (GC-updated) cell address after RetryCustomAllocateOrFail. Also record + // the current cell address and the EXACT same raw interior pointer + // expression that line :245 below passes as `location`. + IndirectHandle poc_cell_handle = + indirect_handle(cell, isolate); + g_poc_host_slot = poc_cell_handle.location(); + g_poc_host_before = (*cell).ptr(); + g_poc_stale_location = + static_cast(&cell->dispatch_handle_); // == `location` arg + // ===================================================================== HeapObject::AllocateAndInstallJSDispatchHandle( cell, &cell->dispatch_handle_, isolate, parameter_count, initial_code); + // === POC INJECTION: disarm =========================================== + g_poc_stale_location = nullptr; + g_poc_host_slot = nullptr; + g_poc_host_before = 0; + // ===================================================================== cells.push_back(cell); }