@@ -74,6 +74,14 @@ extern u64 target_pid_ns_inode;
7474// target_pid_ns_dev is declared in native_stack_trace.ebpf.c
7575extern u64 target_pid_ns_dev ;
7676
77+ extern u32 task_thread_pid_offset ;
78+ extern u32 pid_level_offset ;
79+ extern u32 pid_numbers_offset ;
80+ extern u32 upid_size ;
81+ extern u32 upid_nr_offset ;
82+ extern u32 upid_ns_offset ;
83+ extern u32 pid_namespace_inum_offset ;
84+
7785// Mirrors the kernel's struct bpf_pidns_info for use with bpf_get_ns_current_pid_tgid().
7886// pid: thread PID as seen within the target PID namespace.
7987// tgid: thread group ID (= process PID in userspace) within the target PID namespace.
@@ -82,6 +90,67 @@ struct bpf_pidns_info {
8290 u32 tgid ;
8391};
8492
93+ // Linux permits levels 0 through 32, including the root PID namespace.
94+ #define PID_NAMESPACE_MAX_LEVELS 33
95+
96+ // Resolve the PID of task as visible in the configured target namespace.
97+ static inline EBPF_INLINE bool get_pid_in_target_namespace (u64 task , u32 * result )
98+ {
99+ u64 pid_address = 0 ;
100+ if (
101+ bpf_probe_read_kernel (
102+ & pid_address , sizeof (pid_address ), (void * )(task + task_thread_pid_offset )) ||
103+ pid_address == 0 ) {
104+ return false;
105+ }
106+
107+ u32 active_level = 0 ;
108+ if (bpf_probe_read_kernel (
109+ & active_level , sizeof (active_level ), (void * )(pid_address + pid_level_offset ))) {
110+ return false;
111+ }
112+
113+ for (u32 depth = 0 ; depth < PID_NAMESPACE_MAX_LEVELS ; depth ++ ) {
114+ if (depth > active_level ) {
115+ break ;
116+ }
117+
118+ u32 level = active_level - depth ;
119+ u64 upid_address = pid_address + pid_numbers_offset + ((u64 )level * upid_size );
120+
121+ u64 namespace_address = 0 ;
122+ if (
123+ bpf_probe_read_kernel (
124+ & namespace_address , sizeof (namespace_address ), (void * )(upid_address + upid_ns_offset )) ||
125+ namespace_address == 0 ) {
126+ continue ;
127+ }
128+
129+ u32 namespace_inode = 0 ;
130+ if (
131+ bpf_probe_read_kernel (
132+ & namespace_inode ,
133+ sizeof (namespace_inode ),
134+ (void * )(namespace_address + pid_namespace_inum_offset )) ||
135+ namespace_inode != (u32 )target_pid_ns_inode ) {
136+ continue ;
137+ }
138+
139+ u32 translated_pid = 0 ;
140+ if (
141+ bpf_probe_read_kernel (
142+ & translated_pid , sizeof (translated_pid ), (void * )(upid_address + upid_nr_offset )) ||
143+ translated_pid == 0 ) {
144+ return false;
145+ }
146+
147+ * result = translated_pid ;
148+ return true;
149+ }
150+
151+ return false;
152+ }
153+
85154// get_pid_tgid resolves the current task's PID and TGID, translating them into the
86155// configured target PID namespace if pid_ns_translation_enabled is set. Returns false if
87156// the task could not be resolved (e.g. it is not part of the target namespace), in which
@@ -92,16 +161,29 @@ static inline EBPF_INLINE bool get_pid_tgid(u32 *pid, u32 *tid)
92161 struct bpf_pidns_info ns_info = {0 };
93162 long ret = bpf_get_ns_current_pid_tgid (
94163 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.
164+ if (ret == 0 ) {
165+ // ns_info.tgid is the thread group ID (= process PID in userspace) in the namespace.
166+ // ns_info.pid is the thread PID in the namespace.
167+ // Match the convention of the non-namespace path where pid holds the TGID.
168+ * pid = ns_info .tgid ;
169+ * tid = ns_info .pid ;
170+ return true;
171+ }
172+
173+ u64 task = bpf_get_current_task ();
174+ u64 group_leader = 0 ;
175+ if (
176+ task == 0 ||
177+ bpf_probe_read_kernel (
178+ & group_leader , sizeof (group_leader ), (void * )(task + task_group_leader_offset )) ||
179+ group_leader == 0 ) {
97180 return false;
98181 }
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;
182+
183+ // A helper miss can mean either a descendant namespace or an unrelated
184+ // namespace. Both translations validate the target namespace inode, so
185+ // untranslated host PIDs are never returned from this path.
186+ return get_pid_in_target_namespace (group_leader , pid ) && get_pid_in_target_namespace (task , tid );
105187 }
106188
107189 // bpf_get_current_pid_tgid returns (tgid << 32 | pid).
0 commit comments