1717
1818#ifdef __linux__
1919
20+ #include " counters.h"
2021#include " guards.h"
2122#include " ctimer.h"
2223#include " debugSupport.h"
2324#include " jvmThread.h"
2425#include " libraries.h"
26+ #include " log.h"
2527#include " profiler.h"
28+ #include " signalCookie.h"
2629#include " threadState.inline.h"
2730#include < assert.h>
31+ #include < errno.h>
32+ #include < stddef.h>
2833#include < stdlib.h>
34+ #include < string.h>
2935#include < sys/syscall.h>
3036#include < time.h>
3137#include < unistd.h>
@@ -53,9 +59,25 @@ int CTimer::registerThread(int tid) {
5359 }
5460
5561 struct sigevent sev;
56- sev.sigev_value .sival_ptr = NULL ;
62+ // Zero the whole struct first so any padding / future fields the kernel
63+ // inspects (sigev_notify_function, sigev_notify_attributes on glibc) are
64+ // not populated from stack garbage.
65+ memset (&sev, 0 , sizeof (sev));
66+ // Cookie identifying this timer as ddprof-owned. When the signal is delivered
67+ // the handler checks siginfo->si_value.sival_ptr against SignalCookie::cpu()
68+ // and drops/forwards any SIGPROF that does not carry it (e.g. from a Go
69+ // runtime's setitimer(ITIMER_PROF) or a foreign library's raise()).
70+ sev.sigev_value .sival_ptr = SignalCookie::cpu ();
5771 sev.sigev_signo = _signal;
5872 sev.sigev_notify = SIGEV_THREAD_ID ;
73+ // glibc/musl layout convention: sigev_notify_thread_id sits immediately
74+ // after sigev_notify inside the union — the tid is written as the *second*
75+ // int starting at &sev.sigev_notify, so bytes [sizeof(int), 2*sizeof(int))
76+ // of that int-pointer must be in-bounds of struct sigevent. Guard against
77+ // a future libc change by statically asserting that both ints fit.
78+ static_assert (offsetof (struct sigevent , sigev_notify) + 2 * sizeof (int )
79+ <= sizeof (struct sigevent ),
80+ " sigevent layout assumption broken: tid write would overflow" );
5981 ((int *)&sev.sigev_notify )[1 ] = tid;
6082
6183 // Use raw syscalls, since libc wrapper allows only predefined clocks
@@ -76,7 +98,21 @@ int CTimer::registerThread(int tid) {
7698 ts.it_interval .tv_sec = (time_t )(_interval / 1000000000 );
7799 ts.it_interval .tv_nsec = _interval % 1000000000 ;
78100 ts.it_value = ts.it_interval ;
79- syscall (__NR_timer_settime, timer, 0 , &ts, NULL );
101+ if (syscall (__NR_timer_settime, timer, 0 , &ts, NULL ) < 0 ) {
102+ // Arming failed after publishing the timer in _timers[tid]. Reclaim the
103+ // slot only if it still contains this timer; otherwise a concurrent
104+ // unregisterThread(tid) has already claimed responsibility for cleanup
105+ // (avoids a double timer_delete).
106+ int settime_errno = errno;
107+ char errbuf[64 ];
108+ strerror_r (settime_errno, errbuf, sizeof (errbuf));
109+ Log::warn (" timer_settime failed for tid=%d: %s" , tid, errbuf);
110+ errno = settime_errno;
111+ if (__sync_bool_compare_and_swap (&_timers[tid], timer + 1 , 0 )) {
112+ syscall (__NR_timer_delete, timer);
113+ }
114+ return -1 ;
115+ }
80116 return 0 ;
81117}
82118
@@ -119,6 +155,12 @@ Error CTimer::start(Arguments &args) {
119155 _max_timers = max_timers;
120156 }
121157
158+ // Prime the origin-check cache from this non-signal context before any
159+ // SIGPROF can fire — reading the env var lazily from the handler itself
160+ // would go through a C++ function-local-static guard, which is not
161+ // async-signal-safe.
162+ OS::primeSignalOriginCheck ();
163+
122164 OS::installSignalHandler (_signal, signalHandler);
123165
124166 // Register all existing threads
@@ -143,6 +185,17 @@ void CTimer::stop() {
143185}
144186
145187void CTimer::signalHandler (int signo, siginfo_t *siginfo, void *ucontext) {
188+ // Reject signals that did not originate from our timer_create timers.
189+ // This guards against Go's process-wide setitimer(ITIMER_PROF) and other
190+ // foreign SIGPROF sources that would otherwise drive our handler onto
191+ // threads we never registered — see doc/plans/SignalOriginValidation.md.
192+ if (!OS::shouldProcessSignal (siginfo, SI_TIMER , SignalCookie::cpu ())) {
193+ Counters::increment (CTIMER_SIGNAL_FOREIGN );
194+ OS::forwardForeignSignal (signo, siginfo, ucontext);
195+ return ;
196+ }
197+ Counters::increment (CTIMER_SIGNAL_OWN );
198+
146199 // Atomically try to enter critical section - prevents all reentrancy races
147200 CriticalSection cs;
148201 if (!cs.entered ()) {
0 commit comments