From 179f4e5c46775d48aa1edbc8c2893ed885800d02 Mon Sep 17 00:00:00 2001 From: vichhka-git Date: Tue, 17 Feb 2026 17:00:04 +0700 Subject: [PATCH] Fix segfault from null GetFunction() in DartDumper for Dart 3.10 GetFunction() returns nullptr when an UnlinkedCall target address is not found in the functions or stubs maps. Three call sites in DartDumper.cpp dereferenced the result without null checks, causing SIGSEGV during Object Pool dump on Dart 3.10.7 snapshots. Added null guards with fallback representations at: - getPoolObjectDescription() UnlinkedCall kImmediate handler - DumpStructHeaderFile() UnlinkedCall kImmediate handler - ObjectToString() kFunctionCid handler Also fixed a pre-existing extra argument in std::format() call in DumpStructHeaderFile(). --- blutter/src/DartDumper.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/blutter/src/DartDumper.cpp b/blutter/src/DartDumper.cpp index 5715e29..f97ec35 100644 --- a/blutter/src/DartDumper.cpp +++ b/blutter/src/DartDumper.cpp @@ -192,7 +192,12 @@ std::vector> DartDumper::DumpStructHeaderFile(s if (unlinkTargetType == dart::ObjectPool::EntryType::kImmediate) { const auto imm = pool.RawValueAt(i + 1); auto dartFn = app.GetFunction(imm - app.base()); - name = std::format("UnlinkedCall_{:#x}_{:#x}", offset, dartFn->Address(), offset); + if (dartFn) { + name = std::format("UnlinkedCall_{:#x}_{:#x}", offset, dartFn->Address()); + } + else { + name = std::format("UnlinkedCall_{:#x}_{:#x}", offset, imm - app.base()); + } } else { ASSERT(unlinkTargetType == dart::ObjectPool::EntryType::kTaggedObject); @@ -486,7 +491,11 @@ std::string DartDumper::ObjectToString(dart::Object& obj, bool simpleForm, bool return "SubtypeTestCache"; case dart::kFunctionCid: { // stub never be in Object Pool - auto dartFn = app.GetFunction(dart::Function::Cast(obj).entry_point() - app.base())->AsFunction(); + auto fnBase = app.GetFunction(dart::Function::Cast(obj).entry_point() - app.base()); + if (!fnBase) { + return std::format("Function({:#x})", dart::Function::Cast(obj).entry_point() - app.base()); + } + auto dartFn = fnBase->AsFunction(); if (dartFn->IsClosure()) { auto parentFn = dartFn->GetOutermostFunction(); if (parentFn) { @@ -803,7 +812,12 @@ std::string DartDumper::getPoolObjectDescription(intptr_t offset, bool simpleFor if (unlinkTargetType == dart::ObjectPool::EntryType::kImmediate) { const auto imm = pool.RawValueAt(idx + 1); auto dartFn = app.GetFunction(imm - app.base()); - return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - {}", offset, dartFn->Address(), dartFn->FullName().c_str()); + if (dartFn) { + return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - {}", offset, dartFn->Address(), dartFn->FullName().c_str()); + } + else { + return std::format("[pp+{:#x}] UnlinkedCall: {:#x} - [unknown]", offset, imm - app.base()); + } } else { ASSERT(unlinkTargetType == dart::ObjectPool::EntryType::kTaggedObject);