66#include < cstddef>
77#include < cstring>
88#include " ../../main/cpp/objectSampler.h"
9+ #include " ../../main/cpp/gtest_crash_handler.h"
910#include " vmEntry.h"
1011
12+ static constexpr char OBJECT_SAMPLER_TEST_NAME[] = " ObjectSamplerTest" ;
13+ class ObjectSamplerGlobalSetup {
14+ public:
15+ ObjectSamplerGlobalSetup () { installGtestCrashHandler<OBJECT_SAMPLER_TEST_NAME>(); }
16+ ~ObjectSamplerGlobalSetup () { restoreDefaultSignalHandlers (); }
17+ };
18+ static ObjectSamplerGlobalSetup object_sampler_global_setup;
19+
1120// ---------------------------------------------------------------------------
1221// ObjectSamplerTestAccessor — friend of ObjectSampler, exposes internals
1322// needed by the regression tests.
@@ -46,10 +55,7 @@ class ObjectSamplerDeallocateTest : public ::testing::Test {
4655 _jvmtiEnv mock_env{};
4756 int deallocate_calls = 0 ;
4857
49- // Active fixture pointer used by the C-style mock callbacks to reach
50- // per-instance state. Reset in SetUp/TearDown so the mocks never see a
51- // stale fixture even if a previous test crashed mid-run.
52- static thread_local ObjectSamplerDeallocateTest *active_fixture;
58+ static ObjectSamplerDeallocateTest *active_fixture;
5359
5460 void SetUp () override {
5561 deallocate_calls = 0 ;
@@ -71,22 +77,27 @@ class ObjectSamplerDeallocateTest : public ::testing::Test {
7177 tbl.GetClassSignature = fn;
7278 }
7379
74- // Mock Deallocate increments the per-fixture counter; it never frees the
75- // pointer because the mock signature buffer is statically allocated.
80+ void runAndExpect (
81+ jvmtiError (JNICALL *mock)(jvmtiEnv *, jclass, char **, char **),
82+ bool active, int expected_calls) {
83+ setMockGetClassSignature (mock);
84+ ObjectSampler *s = ObjectSampler::instance ();
85+ ObjectSamplerTestAccessor::setActive (s, active);
86+ ObjectSamplerTestAccessor::callRecordAllocation (
87+ s, &mock_env, nullptr , nullptr , BCI_ALLOC, nullptr , nullptr , 1024 );
88+ EXPECT_EQ (deallocate_calls, expected_calls);
89+ }
90+
7691 static jvmtiError JNICALL mock_Deallocate (jvmtiEnv * /* env*/ ,
7792 unsigned char * /* mem*/ ) {
78- if (active_fixture) {
79- ++active_fixture->deallocate_calls ;
80- }
93+ ++active_fixture->deallocate_calls ;
8194 return JVMTI_ERROR_NONE;
8295 }
8396};
8497
85- thread_local ObjectSamplerDeallocateTest *
98+ ObjectSamplerDeallocateTest *
8699 ObjectSamplerDeallocateTest::active_fixture = nullptr ;
87100
88- // GetClassSignature mock: returns JVMTI_ERROR_NONE and writes
89- // g_mock_class_name into *signature_ptr.
90101static jvmtiError JNICALL mock_GetClassSignature_success (
91102 jvmtiEnv * /* env*/ , jclass /* klass*/ ,
92103 char **signature_ptr, char ** /* generic_ptr*/ ) {
@@ -96,28 +107,24 @@ static jvmtiError JNICALL mock_GetClassSignature_success(
96107 return JVMTI_ERROR_NONE;
97108}
98109
99- // GetClassSignature mock: returns an error AND writes a non-NULL sentinel
100- // into *signature_ptr (the UAF scenario we are guarding against ).
110+ // Returns error but writes a non-NULL sentinel into *signature_ptr — the UAF
111+ // scenario the fix guards against (Deallocate must not be called on error ).
101112static jvmtiError JNICALL mock_GetClassSignature_error_with_sentinel (
102113 jvmtiEnv * /* env*/ , jclass /* klass*/ ,
103114 char **signature_ptr, char ** /* generic_ptr*/ ) {
104115 if (signature_ptr) {
105- *signature_ptr = g_mock_class_name; // sentinel: non-NULL despite error
116+ *signature_ptr = g_mock_class_name;
106117 }
107118 return JVMTI_ERROR_INVALID_CLASS;
108119}
109120
110- // GetClassSignature mock: returns an error and leaves *signature_ptr at NULL.
111121static jvmtiError JNICALL mock_GetClassSignature_error_null (
112122 jvmtiEnv * /* env*/ , jclass /* klass*/ ,
113123 char **signature_ptr, char ** /* generic_ptr*/ ) {
114- // Leave *signature_ptr unchanged (NULL as initialised by recordAllocation).
115124 (void )signature_ptr;
116125 return JVMTI_ERROR_INVALID_CLASS;
117126}
118127
119- // GetClassSignature mock: returns JVMTI_ERROR_NONE but writes NULL into
120- // *signature_ptr — a misbehaving JVMTI impl.
121128static jvmtiError JNICALL mock_GetClassSignature_success_null_name (
122129 jvmtiEnv * /* env*/ , jclass /* klass*/ ,
123130 char **signature_ptr, char ** /* generic_ptr*/ ) {
@@ -208,74 +215,34 @@ TEST(ObjectSamplerTest, NormalizePassesThroughObjectArray) {
208215 EXPECT_EQ (out_len, strlen (" [Ljava/lang/String;" ));
209216}
210217
211- // ---------------------------------------------------------------------------
212218// T-01: GetClassSignature returns error with non-NULL sentinel in *signature_ptr.
213219// Deallocate MUST NOT be called.
214- // ---------------------------------------------------------------------------
215220TEST_F (ObjectSamplerDeallocateTest, DeallocateNotCalledOnErrorWithNonNullSentinel) {
216- setMockGetClassSignature (mock_GetClassSignature_error_with_sentinel);
217- ObjectSampler *s = ObjectSampler::instance ();
218- ObjectSamplerTestAccessor::setActive (s, true );
219- ObjectSamplerTestAccessor::callRecordAllocation (
220- s, &mock_env, nullptr , nullptr , BCI_ALLOC,
221- nullptr , nullptr , 1024 );
222- EXPECT_EQ (deallocate_calls, 0 );
221+ runAndExpect (mock_GetClassSignature_error_with_sentinel, true , 0 );
223222}
224223
225- // ---------------------------------------------------------------------------
226224// T-02: GetClassSignature succeeds with a valid class name.
227225// Deallocate IS called exactly once (on the success path).
228226// Note: lookupClass returns -1 because the class map is empty, so the
229227// method returns without recording — that is the expected behaviour.
230- // ---------------------------------------------------------------------------
231228TEST_F (ObjectSamplerDeallocateTest, DeallocateCalledOnceOnGetClassSignatureSuccess) {
232- setMockGetClassSignature (mock_GetClassSignature_success);
233- ObjectSampler *s = ObjectSampler::instance ();
234- ObjectSamplerTestAccessor::setActive (s, true );
235- ObjectSamplerTestAccessor::callRecordAllocation (
236- s, &mock_env, nullptr , nullptr , BCI_ALLOC,
237- nullptr , nullptr , 1024 );
238- EXPECT_EQ (deallocate_calls, 1 );
229+ runAndExpect (mock_GetClassSignature_success, true , 1 );
239230}
240231
241- // ---------------------------------------------------------------------------
242232// T-03: GetClassSignature fails and leaves class_name at NULL.
243233// Deallocate MUST NOT be called.
244- // ---------------------------------------------------------------------------
245234TEST_F (ObjectSamplerDeallocateTest, DeallocateNotCalledOnErrorWithNullName) {
246- setMockGetClassSignature (mock_GetClassSignature_error_null);
247- ObjectSampler *s = ObjectSampler::instance ();
248- ObjectSamplerTestAccessor::setActive (s, true );
249- ObjectSamplerTestAccessor::callRecordAllocation (
250- s, &mock_env, nullptr , nullptr , BCI_ALLOC,
251- nullptr , nullptr , 1024 );
252- EXPECT_EQ (deallocate_calls, 0 );
235+ runAndExpect (mock_GetClassSignature_error_null, true , 0 );
253236}
254237
255- // ---------------------------------------------------------------------------
256238// T-04: GetClassSignature succeeds but writes NULL into *signature_ptr.
257239// Deallocate MUST NOT be called (the NULL guard in the condition fires).
258- // ---------------------------------------------------------------------------
259240TEST_F (ObjectSamplerDeallocateTest, DeallocateNotCalledWhenSuccessButNullName) {
260- setMockGetClassSignature (mock_GetClassSignature_success_null_name);
261- ObjectSampler *s = ObjectSampler::instance ();
262- ObjectSamplerTestAccessor::setActive (s, true );
263- ObjectSamplerTestAccessor::callRecordAllocation (
264- s, &mock_env, nullptr , nullptr , BCI_ALLOC,
265- nullptr , nullptr , 1024 );
266- EXPECT_EQ (deallocate_calls, 0 );
241+ runAndExpect (mock_GetClassSignature_success_null_name, true , 0 );
267242}
268243
269- // ---------------------------------------------------------------------------
270244// T-05: _active is false — recordAllocation returns immediately.
271245// Deallocate MUST NOT be called.
272- // ---------------------------------------------------------------------------
273246TEST_F (ObjectSamplerDeallocateTest, DeallocateNotCalledWhenNotActive) {
274- setMockGetClassSignature (mock_GetClassSignature_success);
275- ObjectSampler *s = ObjectSampler::instance ();
276- ObjectSamplerTestAccessor::setActive (s, false );
277- ObjectSamplerTestAccessor::callRecordAllocation (
278- s, &mock_env, nullptr , nullptr , BCI_ALLOC,
279- nullptr , nullptr , 1024 );
280- EXPECT_EQ (deallocate_calls, 0 );
247+ runAndExpect (mock_GetClassSignature_success, false , 0 );
281248}
0 commit comments