11#include <php.h>
22#include <main/SAPI.h>
3- #include <stdatomic.h>
43#include "ddtrace.h"
54#include "auto_flush.h"
65#include "compat_string.h"
@@ -31,9 +30,10 @@ struct ddog_InstanceId *ddtrace_sidecar_instance_id;
3130static uint8_t dd_sidecar_formatted_session_id [36 ];
3231
3332// Best-effort pointer for the signal handler (SIGTERM/SIGINT). Set to the first
34- // per-thread connection; never cleared until MSHUTDOWN. Signal context cannot
35- // safely call TSRMLS_FETCH(), so this is the only option.
36- _Atomic(ddog_SidecarTransport * ) ddtrace_sidecar_for_signal = NULL ;
33+ // per-thread connection; never cleared until MSHUTDOWN. Not atomic: concurrent
34+ // shutdown is already a best-effort race for signal handlers, so atomicity of
35+ // the pointer load alone would not prevent the underlying use-after-free.
36+ ddog_SidecarTransport * ddtrace_sidecar_for_signal = NULL ;
3737
3838// Connection mode tracking
3939dd_sidecar_active_mode_t ddtrace_sidecar_active_mode = DD_SIDECAR_CONNECTION_NONE ;
@@ -408,9 +408,8 @@ void ddtrace_sidecar_setup(bool appsec_activation, bool appsec_config) {
408408 }
409409
410410 // Record the first established connection for best-effort signal-handler use.
411- if (DDTRACE_G (sidecar )) {
412- ddog_SidecarTransport * expected = NULL ;
413- atomic_compare_exchange_strong (& ddtrace_sidecar_for_signal , & expected , DDTRACE_G (sidecar ));
411+ if (DDTRACE_G (sidecar ) && !ddtrace_sidecar_for_signal ) {
412+ ddtrace_sidecar_for_signal = DDTRACE_G (sidecar );
414413 }
415414}
416415
@@ -447,7 +446,7 @@ void ddtrace_sidecar_handle_fork(void) {
447446 ddog_sidecar_transport_drop (DDTRACE_G (sidecar ));
448447 DDTRACE_G (sidecar ) = NULL ;
449448 }
450- atomic_store ( & ddtrace_sidecar_for_signal , NULL ) ;
449+ ddtrace_sidecar_for_signal = NULL ;
451450
452451 if (ddtrace_sidecar_active_mode == DD_SIDECAR_CONNECTION_THREAD ) {
453452 ddtrace_ffi_try ("Failed clearing inherited listener state" ,
@@ -488,7 +487,7 @@ void ddtrace_sidecar_handle_fork(void) {
488487 }
489488
490489 if (DDTRACE_G (sidecar )) {
491- atomic_store ( & ddtrace_sidecar_for_signal , DDTRACE_G (sidecar ) );
490+ ddtrace_sidecar_for_signal = DDTRACE_G (sidecar );
492491 }
493492#endif
494493}
@@ -500,9 +499,8 @@ void ddtrace_sidecar_ensure_active(void) {
500499 // First RINIT on this thread: the process-level setup already ran (endpoint is
501500 // set), so establish this thread's own connection now.
502501 DDTRACE_G (sidecar ) = ddtrace_sidecar_connect (false);
503- if (DDTRACE_G (sidecar )) {
504- ddog_SidecarTransport * expected = NULL ;
505- atomic_compare_exchange_strong (& ddtrace_sidecar_for_signal , & expected , DDTRACE_G (sidecar ));
502+ if (DDTRACE_G (sidecar ) && !ddtrace_sidecar_for_signal ) {
503+ ddtrace_sidecar_for_signal = DDTRACE_G (sidecar );
506504 }
507505 }
508506}
@@ -528,6 +526,8 @@ void ddtrace_sidecar_finalize(bool clear_id) {
528526}
529527
530528void ddtrace_sidecar_shutdown (void ) {
529+ ddtrace_sidecar_for_signal = NULL ;
530+
531531 // In thread mode, drop the main thread's connection before shutting down the
532532 // listener to avoid deadlock. GSHUTDOWN owns transport cleanup for all other
533533 // threads; the main thread's GSHUTDOWN runs after MSHUTDOWN on some SAPIs,
@@ -542,6 +542,8 @@ void ddtrace_sidecar_shutdown(void) {
542542 current_pid == ddtrace_sidecar_master_pid ) {
543543
544544 if (DDTRACE_G (sidecar )) {
545+ ddtrace_sidecar_for_signal = NULL ;
546+
545547 ddog_sidecar_transport_drop (DDTRACE_G (sidecar ));
546548 DDTRACE_G (sidecar ) = NULL ;
547549 }
@@ -556,8 +558,6 @@ void ddtrace_sidecar_shutdown(void) {
556558 ddtrace_sidecar_instance_id = NULL ;
557559 }
558560
559- atomic_store (& ddtrace_sidecar_for_signal , NULL );
560-
561561 if (ddtrace_endpoint ) {
562562 dd_free_endpoints ();
563563 }
@@ -875,6 +875,10 @@ void ddtrace_sidecar_rshutdown(void) {
875875
876876void ddtrace_sidecar_gshutdown (void ) {
877877 if (DDTRACE_G (sidecar )) {
878+ if (DDTRACE_G (sidecar ) == ddtrace_sidecar_for_signal ) {
879+ ddtrace_sidecar_for_signal = NULL ;
880+ }
881+
878882 // Drain any accumulated background-sender metrics before the transport goes away.
879883 ddtrace_telemetry_flush_bgs_metrics_final ();
880884 ddog_sidecar_transport_drop (DDTRACE_G (sidecar ));
0 commit comments