44#define _GNU_SOURCE
55#include <dlfcn.h>
66#include <errno.h>
7+ #include <execinfo.h>
78#include <fcntl.h>
89#include <pthread.h>
10+ #include <signal.h>
911#include <stddef.h>
1012#include <stdio.h>
13+ #include <stdlib.h>
1114#include <string.h>
1215#include <sys/syscall.h>
1316#include <sys/types.h>
@@ -18,6 +21,7 @@ static void (*real_free)(void *) = NULL;
1821static void * (* real_calloc )(size_t , size_t ) = NULL ;
1922static void * (* real_realloc )(void * , size_t ) = NULL ;
2023static pthread_once_t init_once = PTHREAD_ONCE_INIT ;
24+ static pthread_once_t detection_once = PTHREAD_ONCE_INIT ;
2125
2226// We should load all the real symbols on library load
2327static void init_function_ptrs (void ) {
@@ -35,64 +39,65 @@ __attribute__((constructor)) static void preload_ctor(void) {
3539
3640static int log_fd = -1 ;
3741// Flag to indicate we are currently in the collector; We should only
38- // log when we are in the collector.
42+ // detect allocations when we are in the collector.
3943static int collector_marked = 0 ;
44+ // Flag to track if we've already detected and reported an allocation
45+ // This guards against reentrancy of the detection logic.
46+ static int allocation_detected = 0 ;
4047
41-
42- static void init_logger (void ) {
48+ // Called by the collector process to enable detection in the collector only
49+ void dd_preload_logger_mark_collector (void ) {
50+ collector_marked = 1 ;
4351 if (log_fd >= 0 || !collector_marked ) {
4452 // Already initialized or not a collector
4553 return ;
4654 }
47-
48- const char * path = "/tmp/preload_logger.log" ;
49- log_fd = open (path , O_CREAT | O_WRONLY | O_TRUNC , 0644 );
50- if (log_fd >= 0 ) {
51- char buf [256 ];
52- pid_t pid = getpid ();
53- int len = snprintf (buf , sizeof (buf ), "[DEBUG] Collector logger initialized pid=%d, fd=%d, path=%s\n" ,
54- pid , log_fd , path );
55- write (log_fd , buf , len );
56- }
57- }
58-
59- // Called by the collector process to scope logging to the collector only
60- void dd_preload_logger_mark_collector (void ) {
61- collector_marked = 1 ;
62-
63- init_logger ();
64-
65- if (log_fd >= 0 ) {
66- char buf [256 ];
67- pid_t pid = getpid ();
68- int len = snprintf (buf , sizeof (buf ), "[DEBUG] Marked as collector, pid=%d, fd=%d\n" , pid , log_fd );
69- write (log_fd , buf , len );
70- }
7155}
7256
73- static void log_line (const char * tag , size_t size , void * ptr ) {
74- if (log_fd < 0 ) {
75- return ;
57+ static void capture_and_report_allocation (const char * func_name ) {
58+ // Only report once using atomic compare-and-swap
59+ if (__sync_bool_compare_and_swap (& allocation_detected , 0 , 1 )) {
60+ const char * path = "/tmp/preload_detector.log" ;
61+ log_fd = open (path , O_CREAT | O_WRONLY | O_TRUNC , 0644 );
62+ if (log_fd >= 0 ) {
63+ char buf [4096 ];
64+ pid_t pid = getpid ();
65+ long tid = syscall (SYS_gettid );
66+
67+ // Log the detection
68+ int len = snprintf (buf , sizeof (buf ),
69+ "[FATAL] Dangerous allocation detected in collector!\n"
70+ " Function: %s\n"
71+ " PID: %d\n"
72+ " TID: %ld\n"
73+ " Stacktrace:\n" ,
74+ func_name , pid , tid );
75+ write (log_fd , buf , len );
76+
77+ // Capture and log stacktrace
78+ void * array [100 ];
79+ int size = backtrace (array , 100 );
80+ char * * strings = backtrace_symbols (array , size );
81+
82+ if (strings != NULL ) {
83+ for (int i = 0 ; i < size ; i ++ ) {
84+ len = snprintf (buf , sizeof (buf ), " #%d %s\n" , i , strings [i ]);
85+ write (log_fd , buf , len );
86+ }
87+ // Note: we can't use free() here since we're in an allocation hook
88+ // backtrace_symbols uses malloc internally, so we have a small leak
89+ // but this is acceptable since this only happens once and we guard
90+ // against it anyways
91+ }
92+
93+ fsync (log_fd );
94+ close (log_fd );
95+ log_fd = -1 ;
96+ }
7697 }
7798
78- char buf [256 ];
79- pid_t pid = getpid ();
80- long tid = syscall (SYS_gettid );
81- int len = 0 ;
82-
83- if (strcmp (tag , "malloc" ) == 0 ) {
84- len = snprintf (buf , sizeof (buf ), "pid=%d tid=%ld malloc size=%zu ptr=%p\n" , pid , tid , size , ptr );
85- } else if (strcmp (tag , "calloc" ) == 0 ) {
86- len = snprintf (buf , sizeof (buf ), "pid=%d tid=%ld calloc size=%zu ptr=%p\n" , pid , tid , size , ptr );
87- } else if (strcmp (tag , "realloc" ) == 0 ) {
88- len = snprintf (buf , sizeof (buf ), "pid=%d tid=%ld realloc size=%zu ptr=%p\n" , pid , tid , size , ptr );
89- } else if (strcmp (tag , "free" ) == 0 ) {
90- len = snprintf (buf , sizeof (buf ), "pid=%d tid=%ld free ptr=%p\n" , pid , tid , ptr );
91- }
92-
93- if (len > 0 ) {
94- (void )write (log_fd , buf , (size_t )len );
95- }
99+ // Don't abort. let the collector continue so it can finish writing the crash report
100+ // The test will check for the log file and fail if allocations were detected
96101}
97102
98103void * malloc (size_t size ) {
@@ -101,10 +106,11 @@ void *malloc(size_t size) {
101106 return NULL ;
102107 }
103108
104- void * ptr = real_malloc (size );
105109 if (collector_marked ) {
106- log_line ("malloc" , size , ptr );
110+ capture_and_report_allocation ("malloc" );
107111 }
112+
113+ void * ptr = real_malloc (size );
108114 return ptr ;
109115}
110116
@@ -113,9 +119,7 @@ void free(void *ptr) {
113119 return ;
114120 }
115121
116- if (collector_marked ) {
117- log_line ("free" , 0 , ptr );
118- }
122+ // free is generally safe; we'll allow free operations without failing
119123 real_free (ptr );
120124}
121125
@@ -125,10 +129,11 @@ void *calloc(size_t nmemb, size_t size) {
125129 return NULL ;
126130 }
127131
128- void * ptr = real_calloc (nmemb , size );
129132 if (collector_marked ) {
130- log_line ("calloc" , nmemb * size , ptr );
133+ capture_and_report_allocation ("calloc" );
131134 }
135+
136+ void * ptr = real_calloc (nmemb , size );
132137 return ptr ;
133138}
134139
@@ -138,9 +143,10 @@ void *realloc(void *ptr, size_t size) {
138143 return NULL ;
139144 }
140145
141- void * new_ptr = real_realloc (ptr , size );
142146 if (collector_marked ) {
143- log_line ("realloc" , size , new_ptr );
147+ capture_and_report_allocation ("realloc" );
144148 }
149+
150+ void * new_ptr = real_realloc (ptr , size );
145151 return new_ptr ;
146152}
0 commit comments