@@ -68,12 +68,22 @@ extern u16 origin_id_sampling;
6868// pid_ns_translation_enabled is declared in native_stack_trace.ebpf.c
6969extern bool pid_ns_translation_enabled ;
7070
71+ extern bool translate_descendant_pids ;
72+
7173// target_pid_ns_inode is declared in native_stack_trace.ebpf.c
7274extern u64 target_pid_ns_inode ;
7375
7476// target_pid_ns_dev is declared in native_stack_trace.ebpf.c
7577extern u64 target_pid_ns_dev ;
7678
79+ extern u32 task_thread_pid_offset ;
80+ extern u32 pid_level_offset ;
81+ extern u32 pid_numbers_offset ;
82+ extern u32 upid_size ;
83+ extern u32 upid_nr_offset ;
84+ extern u32 upid_ns_offset ;
85+ extern u32 pid_namespace_inum_offset ;
86+
7787// Mirrors the kernel's struct bpf_pidns_info for use with bpf_get_ns_current_pid_tgid().
7888// pid: thread PID as seen within the target PID namespace.
7989// tgid: thread group ID (= process PID in userspace) within the target PID namespace.
@@ -82,6 +92,67 @@ struct bpf_pidns_info {
8292 u32 tgid ;
8393};
8494
95+ // Linux permits levels 0 through 32, including the root PID namespace.
96+ #define PID_NAMESPACE_MAX_LEVELS 33
97+
98+ // Resolve the PID of task as visible in the configured target namespace.
99+ static inline EBPF_INLINE bool get_pid_in_target_namespace (u64 task , u32 * result )
100+ {
101+ u64 pid_address = 0 ;
102+ if (
103+ bpf_probe_read_kernel (
104+ & pid_address , sizeof (pid_address ), (void * )(task + task_thread_pid_offset )) ||
105+ pid_address == 0 ) {
106+ return false;
107+ }
108+
109+ u32 active_level = 0 ;
110+ if (bpf_probe_read_kernel (
111+ & active_level , sizeof (active_level ), (void * )(pid_address + pid_level_offset ))) {
112+ return false;
113+ }
114+
115+ for (u32 depth = 0 ; depth < PID_NAMESPACE_MAX_LEVELS ; depth ++ ) {
116+ if (depth > active_level ) {
117+ break ;
118+ }
119+
120+ u32 level = active_level - depth ;
121+ u64 upid_address = pid_address + pid_numbers_offset + ((u64 )level * upid_size );
122+
123+ u64 namespace_address = 0 ;
124+ if (
125+ bpf_probe_read_kernel (
126+ & namespace_address , sizeof (namespace_address ), (void * )(upid_address + upid_ns_offset )) ||
127+ namespace_address == 0 ) {
128+ continue ;
129+ }
130+
131+ u32 namespace_inode = 0 ;
132+ if (
133+ bpf_probe_read_kernel (
134+ & namespace_inode ,
135+ sizeof (namespace_inode ),
136+ (void * )(namespace_address + pid_namespace_inum_offset )) ||
137+ namespace_inode != (u32 )target_pid_ns_inode ) {
138+ continue ;
139+ }
140+
141+ u32 translated_pid = 0 ;
142+ if (
143+ bpf_probe_read_kernel (
144+ & translated_pid , sizeof (translated_pid ), (void * )(upid_address + upid_nr_offset )) ||
145+ translated_pid == 0 ) {
146+ return false;
147+ }
148+
149+ * result = translated_pid ;
150+ return true;
151+ }
152+
153+ return false;
154+ }
155+
85156// get_pid_tgid resolves the current task's PID and TGID, translating them into the
86157// configured target PID namespace if pid_ns_translation_enabled is set. Returns false if
87158// the task could not be resolved (e.g. it is not part of the target namespace), in which
@@ -92,16 +163,33 @@ static inline EBPF_INLINE bool get_pid_tgid(u32 *pid, u32 *tid)
92163 struct bpf_pidns_info ns_info = {0 };
93164 long ret = bpf_get_ns_current_pid_tgid (
94165 target_pid_ns_dev , target_pid_ns_inode , & ns_info , sizeof (ns_info ));
95- if (ret < 0 ) {
96- // Task is not in the target namespace, signal caller to skip it.
166+ if (ret == 0 ) {
167+ // ns_info.tgid is the thread group ID (= process PID in userspace) in the namespace.
168+ // ns_info.pid is the thread PID in the namespace.
169+ // Match the convention of the non-namespace path where pid holds the TGID.
170+ * pid = ns_info .tgid ;
171+ * tid = ns_info .pid ;
172+ return true;
173+ }
174+
175+ if (!translate_descendant_pids ) {
97176 return false;
98177 }
99- // ns_info.tgid is the thread group ID (= process PID in userspace) in the namespace.
100- // ns_info.pid is the thread PID in the namespace.
101- // Match the convention of the non-namespace path where pid holds the TGID.
102- * pid = ns_info .tgid ;
103- * tid = ns_info .pid ;
104- return true;
178+
179+ u64 task = bpf_get_current_task ();
180+ u64 group_leader = 0 ;
181+ if (
182+ task == 0 ||
183+ bpf_probe_read_kernel (
184+ & group_leader , sizeof (group_leader ), (void * )(task + task_group_leader_offset )) ||
185+ group_leader == 0 ) {
186+ return false;
187+ }
188+
189+ // A helper miss can mean either a descendant namespace or an unrelated
190+ // namespace. Both translations validate the target namespace inode, so
191+ // untranslated host PIDs are never returned from this path.
192+ return get_pid_in_target_namespace (group_leader , pid ) && get_pid_in_target_namespace (task , tid );
105193 }
106194
107195 // bpf_get_current_pid_tgid returns (tgid << 32 | pid).
0 commit comments