1+ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache License (Version 2.0).
2+ // This product includes software developed at Datadog (https://www.datadoghq.com/) Copyright 2025 Datadog, Inc.
3+
14#include " otel_process_ctx.h"
25
3- #include < limits.h>
6+ #ifndef _GNU_SOURCE
7+ #define _GNU_SOURCE
8+ #endif
9+
410#ifdef __cplusplus
511 #include < atomic>
612 using std::atomic_thread_fence;
@@ -42,7 +48,7 @@ static const otel_process_ctx_data empty_data = {
4248 return (otel_process_ctx_result) {.success = false , .error_message = " OTEL_PROCESS_CTX_NOOP mode is enabled - no-op implementation (" __FILE__ " :" ADD_QUOTES (__LINE__) " )" };
4349 }
4450
45- bool otel_process_ctx_drop (void ) {
51+ bool otel_process_ctx_drop_current (void ) {
4652 return true ; // Nothing to do, this always succeeds
4753 }
4854
@@ -99,7 +105,7 @@ static otel_process_ctx_state published_state;
99105
100106static otel_process_ctx_result otel_process_ctx_encode_payload (char **out, uint32_t *out_size, otel_process_ctx_data data);
101107
102- // We use a mapping size of 3 pages explicitly as a hint when running on legacy kernels that don't support the
108+ // We use a mapping size of 2 pages explicitly as a hint when running on legacy kernels that don't support the
103109// PR_SET_VMA_ANON_NAME prctl call; see below for more details.
104110static long size_for_mapping (void ) {
105111 long page_size_bytes = sysconf (_SC_PAGESIZE);
@@ -208,7 +214,7 @@ bool otel_process_ctx_drop_current(void) {
208214 // (due to the MADV_DONTFORK) and we don't need to do anything to it.
209215 if (state.mapping != NULL && state.mapping != MAP_FAILED && getpid () == state.publisher_pid ) {
210216 long mapping_size = size_for_mapping ();
211- if (mapping_size == -1 || munmap (published_state .mapping , mapping_size) == -1 ) return false ;
217+ if (mapping_size == -1 || munmap (state .mapping , mapping_size) == -1 ) return false ;
212218 }
213219
214220 // The payload may have been inherited from a parent. This is a regular malloc so we need to free it so we don't leak.
@@ -321,15 +327,20 @@ static otel_process_ctx_result otel_process_ctx_encode_payload(char **out, uint3
321327}
322328
323329#ifndef OTEL_PROCESS_CTX_NO_READ
330+ #include < inttypes.h>
331+ #include < limits.h>
332+ #include < sys/uio.h>
333+ #include < sys/utsname.h>
334+
324335 // Note: The below parsing code is only for otel_process_ctx_read and is only provided for debugging
325336 // and testing purposes.
326337
327- static bool is_otel_process_ctx_mapping ( char *line) {
328- size_t name_len = sizeof ( " [anon:OTEL_CTX] " ) - 1 ;
329- size_t line_len = strlen (line) ;
330- if (line_len < name_len) return false ;
331- if (line[line_len- 1 ] == ' \n ' ) line[--line_len] = ' \0 ' ;
332- return memcmp (line + (line_len - name_len), " [anon:OTEL_CTX] " , name_len) == 0 ;
338+ // Named mappings are supported on Linux 5.17+
339+ static bool named_mapping_supported ( void ) {
340+ struct utsname uts ;
341+ int major, minor ;
342+ if (uname (&uts) != 0 || sscanf (uts. release , " %d.%d " , &major, &minor) != 2 ) return false ;
343+ return (major > 5 ) || (major == 5 && minor >= 17 ) ;
333344 }
334345
335346 static void *parse_mapping_start (char *line) {
@@ -339,6 +350,41 @@ static otel_process_ctx_result otel_process_ctx_encode_payload(char **out, uint3
339350 return (void *)(uintptr_t ) start;
340351 }
341352
353+ static bool is_otel_process_ctx_mapping (char *line) {
354+ size_t name_len = sizeof (" [anon:OTEL_CTX]" ) - 1 ;
355+ size_t line_len = strlen (line);
356+ if (line_len < name_len) return false ;
357+ if (line[line_len-1 ] == ' \n ' ) line[--line_len] = ' \0 ' ;
358+
359+ // Validate expected permission
360+ if (strstr (line, " r--p " ) == NULL ) return false ;
361+
362+ // Validate expected context size
363+ int64_t start, end;
364+ if (sscanf (line, " %" PRIx64 " -%" PRIx64, &start, &end) != 2 ) return false ;
365+ if (start == 0 || end == 0 || end <= start) return false ;
366+ if ((end - start) != size_for_mapping ()) return false ;
367+
368+ if (named_mapping_supported ()) {
369+ // On Linux 5.17+, check if the line ends with [anon:OTEL_CTX]
370+ return memcmp (line + (line_len - name_len), " [anon:OTEL_CTX]" , name_len) == 0 ;
371+ } else {
372+ // On older kernels, parse the address to to find the OTEL_CTX signature
373+ void *addr = parse_mapping_start (line);
374+ if (addr == NULL ) return false ;
375+
376+ // Read 8 bytes at the address using process_vm_readv (to avoid any issues with concurrency/races)
377+ char buffer[8 ];
378+ struct iovec local[] = {{.iov_base = buffer, .iov_len = sizeof (buffer)}};
379+ struct iovec remote[] = {{.iov_base = addr, .iov_len = sizeof (buffer)}};
380+
381+ ssize_t bytes_read = process_vm_readv (getpid (), local, 1 , remote, 1 , 0 );
382+ if (bytes_read != sizeof (buffer)) return false ;
383+
384+ return memcmp (buffer, " OTEL_CTX" , sizeof (buffer)) == 0 ;
385+ }
386+ }
387+
342388 static otel_process_ctx_mapping *try_finding_mapping (void ) {
343389 char line[8192 ];
344390 otel_process_ctx_mapping *result = NULL ;
0 commit comments