From a5e07eabaa11d837757c27e1a779b0a876960ef0 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Mon, 13 Jul 2026 15:06:11 +0200 Subject: [PATCH 1/2] Fix: gate FrameType::isRawPointer on HotSpot The raw-pointer bci encoding (bit 30) only ever means "jmethodID slot replaced by a VMMethod*" on HotSpot. On OpenJ9, an unencoded, VM-supplied bci can coincidentally have bit 30 set, which was misread as our raw-pointer encoding and routed into JVMSupport::resolve(), which asserts/aborts for non-HotSpot VMs. Reproduced on Semeru 8.0.462/OpenJ9 via GetLineNumberTableLeakTest. Co-Authored-By: Claude Sonnet 5 --- ddprof-lib/src/main/cpp/frame.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ddprof-lib/src/main/cpp/frame.h b/ddprof-lib/src/main/cpp/frame.h index 574ca41a1..5c00603ac 100644 --- a/ddprof-lib/src/main/cpp/frame.h +++ b/ddprof-lib/src/main/cpp/frame.h @@ -79,8 +79,11 @@ class FrameType { return (FrameTypeId)((bci >> TYPE_SHIFT) & FRAME_TYPE_MASK); } + // Raw pointers only exist on HotSpot (see hotspotSupport.cpp fillFrameRaw). + // On other VMs an unencoded, VM-supplied bci can coincidentally have bit 30 + // set; that must not be mistaken for our raw-pointer encoding. static inline bool isRawPointer(int bci) { - return bci > 0 && (bci & RAW_POINTER_MASK) != 0; + return VM::isHotspot() && bci > 0 && (bci & RAW_POINTER_MASK) != 0; } }; From 552da8c8bbdcbcbc3ff923ba7c13e7540aafe468 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Mon, 13 Jul 2026 15:24:17 +0200 Subject: [PATCH 2/2] Fix frame_ut for isRawPointer() hotspot gating TrueWhenBit30IsSet assumed isRawPointer() ignores VM kind; split into hotspot/non-hotspot cases now that it's gated on VM::isHotspot(). --- ddprof-lib/src/test/cpp/frame_ut.cpp | 31 +++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/ddprof-lib/src/test/cpp/frame_ut.cpp b/ddprof-lib/src/test/cpp/frame_ut.cpp index c31d18f77..5b96efb83 100644 --- a/ddprof-lib/src/test/cpp/frame_ut.cpp +++ b/ddprof-lib/src/test/cpp/frame_ut.cpp @@ -7,6 +7,14 @@ #include "../../main/cpp/frame.h" #include "../../main/cpp/gtest_crash_handler.h" +// VMTestAccessor — friend of VM, lets tests toggle VM::isHotspot() since +// isRawPointer() now gates on it (raw pointers only exist on HotSpot). +class VMTestAccessor { +public: + static bool getHotspot() { return VM::_hotspot; } + static void setHotspot(bool v) { VM::_hotspot = v; } +}; + static constexpr char FRAME_TEST_NAME[] = "FrameTest"; class GlobalSetup { @@ -139,12 +147,29 @@ TEST(FrameTypeIsRawPointerTest, FalseForEncodedWithoutFlag) { } } -TEST(FrameTypeIsRawPointerTest, TrueWhenBit30IsSet) { - // Manually set the raw-pointer flag (bit 30) on an encoded value +TEST(FrameTypeIsRawPointerTest, TrueWhenBit30IsSetOnHotspot) { + // Manually set the raw-pointer flag (bit 30) on an encoded value. + // Raw pointers only exist on HotSpot, so isRawPointer() must be gated on it. + bool saved = VMTestAccessor::getHotspot(); + VMTestAccessor::setHotspot(true); int base = FrameType::encode(FRAME_JIT_COMPILED, 0); int withFlag = base | (1 << 30); EXPECT_TRUE(FrameType::isRawPointer(withFlag)) - << "isRawPointer() must be true when bit 30 is set and value is positive"; + << "isRawPointer() must be true when bit 30 is set, value is positive, and VM is HotSpot"; + VMTestAccessor::setHotspot(saved); +} + +TEST(FrameTypeIsRawPointerTest, FalseWhenBit30IsSetOnNonHotspot) { + // Non-HotSpot VMs can coincidentally produce an unencoded bci with bit 30 + // set (e.g. OpenJ9's raw AsyncGetCallTrace output); that must not be + // mistaken for HotSpot's raw-pointer encoding. + bool saved = VMTestAccessor::getHotspot(); + VMTestAccessor::setHotspot(false); + int base = FrameType::encode(FRAME_JIT_COMPILED, 0); + int withFlag = base | (1 << 30); + EXPECT_FALSE(FrameType::isRawPointer(withFlag)) + << "isRawPointer() must be false when VM is not HotSpot, even with bit 30 set"; + VMTestAccessor::setHotspot(saved); } TEST(FrameTypeIsRawPointerTest, FalseWithOnlyBit30SetOnNegative) {