Skip to content

Commit 4823b72

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf_get_ns_current_pid_tgid'
Carlos Neira says: ==================== Currently bpf_get_current_pid_tgid(), is used to do pid filtering in bcc's scripts but this helper returns the pid as seen by the root namespace which is fine when a bcc script is not executed inside a container. When the process of interest is inside a container, pid filtering will not work if bpf_get_current_pid_tgid() is used. This helper addresses this limitation returning the pid as it's seen by the current namespace where the script is executing. In the future different pid_ns files may belong to different devices, according to the discussion between Eric Biederman and Yonghong in 2017 Linux plumbers conference. To address that situation the helper requires inum and dev_t from /proc/self/ns/pid. This helper has the same use cases as bpf_get_current_pid_tgid() as it can be used to do pid filtering even inside a container. ==================== Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 132c1af + 1c1052e commit 4823b72

File tree

14 files changed

+391
-3
lines changed

14 files changed

+391
-3
lines changed

fs/nsfs.c

+14
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,20 @@ struct file *proc_ns_fget(int fd)
247247
return ERR_PTR(-EINVAL);
248248
}
249249

250+
/**
251+
* ns_match() - Returns true if current namespace matches dev/ino provided.
252+
* @ns_common: current ns
253+
* @dev: dev_t from nsfs that will be matched against current nsfs
254+
* @ino: ino_t from nsfs that will be matched against current nsfs
255+
*
256+
* Return: true if dev and ino matches the current nsfs.
257+
*/
258+
bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino)
259+
{
260+
return (ns->inum == ino) && (nsfs_mnt->mnt_sb->s_dev == dev);
261+
}
262+
263+
250264
static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry)
251265
{
252266
struct inode *inode = d_inode(dentry);

include/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,7 @@ extern const struct bpf_func_proto bpf_strtol_proto;
14971497
extern const struct bpf_func_proto bpf_strtoul_proto;
14981498
extern const struct bpf_func_proto bpf_tcp_sock_proto;
14991499
extern const struct bpf_func_proto bpf_jiffies64_proto;
1500+
extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto;
15001501

15011502
/* Shared helpers among cBPF and eBPF. */
15021503
void bpf_user_rnd_init_once(void);

include/linux/proc_ns.h

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ typedef struct ns_common *ns_get_path_helper_t(void *);
8585
extern int ns_get_path_cb(struct path *path, ns_get_path_helper_t ns_get_cb,
8686
void *private_data);
8787

88+
extern bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino);
89+
8890
extern int ns_get_name(char *buf, size_t size, struct task_struct *task,
8991
const struct proc_ns_operations *ns_ops);
9092
extern void nsfs_init(void);

include/uapi/linux/bpf.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -2914,6 +2914,19 @@ union bpf_attr {
29142914
* of sizeof(struct perf_branch_entry).
29152915
*
29162916
* **-ENOENT** if architecture does not support branch records.
2917+
*
2918+
* int bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size)
2919+
* Description
2920+
* Returns 0 on success, values for *pid* and *tgid* as seen from the current
2921+
* *namespace* will be returned in *nsdata*.
2922+
*
2923+
* On failure, the returned value is one of the following:
2924+
*
2925+
* **-EINVAL** if dev and inum supplied don't match dev_t and inode number
2926+
* with nsfs of current task, or if dev conversion to dev_t lost high bits.
2927+
*
2928+
* **-ENOENT** if pidns does not exists for the current task.
2929+
*
29172930
*/
29182931
#define __BPF_FUNC_MAPPER(FN) \
29192932
FN(unspec), \
@@ -3035,7 +3048,8 @@ union bpf_attr {
30353048
FN(tcp_send_ack), \
30363049
FN(send_signal_thread), \
30373050
FN(jiffies64), \
3038-
FN(read_branch_records),
3051+
FN(read_branch_records), \
3052+
FN(get_ns_current_pid_tgid),
30393053

30403054
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
30413055
* function eBPF program intends to call
@@ -3829,4 +3843,8 @@ struct bpf_sockopt {
38293843
__s32 retval;
38303844
};
38313845

3846+
struct bpf_pidns_info {
3847+
__u32 pid;
3848+
__u32 tgid;
3849+
};
38323850
#endif /* _UAPI__LINUX_BPF_H__ */

kernel/bpf/core.c

+1
Original file line numberDiff line numberDiff line change
@@ -2149,6 +2149,7 @@ const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak;
21492149
const struct bpf_func_proto bpf_get_current_comm_proto __weak;
21502150
const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak;
21512151
const struct bpf_func_proto bpf_get_local_storage_proto __weak;
2152+
const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto __weak;
21522153

21532154
const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
21542155
{

kernel/bpf/helpers.c

+45
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <linux/filter.h>
1313
#include <linux/ctype.h>
1414
#include <linux/jiffies.h>
15+
#include <linux/pid_namespace.h>
16+
#include <linux/proc_ns.h>
1517

1618
#include "../../lib/kstrtox.h"
1719

@@ -499,3 +501,46 @@ const struct bpf_func_proto bpf_strtoul_proto = {
499501
.arg4_type = ARG_PTR_TO_LONG,
500502
};
501503
#endif
504+
505+
BPF_CALL_4(bpf_get_ns_current_pid_tgid, u64, dev, u64, ino,
506+
struct bpf_pidns_info *, nsdata, u32, size)
507+
{
508+
struct task_struct *task = current;
509+
struct pid_namespace *pidns;
510+
int err = -EINVAL;
511+
512+
if (unlikely(size != sizeof(struct bpf_pidns_info)))
513+
goto clear;
514+
515+
if (unlikely((u64)(dev_t)dev != dev))
516+
goto clear;
517+
518+
if (unlikely(!task))
519+
goto clear;
520+
521+
pidns = task_active_pid_ns(task);
522+
if (unlikely(!pidns)) {
523+
err = -ENOENT;
524+
goto clear;
525+
}
526+
527+
if (!ns_match(&pidns->ns, (dev_t)dev, ino))
528+
goto clear;
529+
530+
nsdata->pid = task_pid_nr_ns(task, pidns);
531+
nsdata->tgid = task_tgid_nr_ns(task, pidns);
532+
return 0;
533+
clear:
534+
memset((void *)nsdata, 0, (size_t) size);
535+
return err;
536+
}
537+
538+
const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto = {
539+
.func = bpf_get_ns_current_pid_tgid,
540+
.gpl_only = false,
541+
.ret_type = RET_INTEGER,
542+
.arg1_type = ARG_ANYTHING,
543+
.arg2_type = ARG_ANYTHING,
544+
.arg3_type = ARG_PTR_TO_UNINIT_MEM,
545+
.arg4_type = ARG_CONST_SIZE,
546+
};

kernel/trace/bpf_trace.c

+2
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,8 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
843843
return &bpf_send_signal_thread_proto;
844844
case BPF_FUNC_perf_event_read_value:
845845
return &bpf_perf_event_read_value_proto;
846+
case BPF_FUNC_get_ns_current_pid_tgid:
847+
return &bpf_get_ns_current_pid_tgid_proto;
846848
default:
847849
return NULL;
848850
}

scripts/bpf_helpers_doc.py

+1
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,7 @@ class PrinterHelpers(Printer):
435435
'struct bpf_fib_lookup',
436436
'struct bpf_perf_event_data',
437437
'struct bpf_perf_event_value',
438+
'struct bpf_pidns_info',
438439
'struct bpf_sock',
439440
'struct bpf_sock_addr',
440441
'struct bpf_sock_ops',

tools/include/uapi/linux/bpf.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -2914,6 +2914,19 @@ union bpf_attr {
29142914
* of sizeof(struct perf_branch_entry).
29152915
*
29162916
* **-ENOENT** if architecture does not support branch records.
2917+
*
2918+
* int bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size)
2919+
* Description
2920+
* Returns 0 on success, values for *pid* and *tgid* as seen from the current
2921+
* *namespace* will be returned in *nsdata*.
2922+
*
2923+
* On failure, the returned value is one of the following:
2924+
*
2925+
* **-EINVAL** if dev and inum supplied don't match dev_t and inode number
2926+
* with nsfs of current task, or if dev conversion to dev_t lost high bits.
2927+
*
2928+
* **-ENOENT** if pidns does not exists for the current task.
2929+
*
29172930
*/
29182931
#define __BPF_FUNC_MAPPER(FN) \
29192932
FN(unspec), \
@@ -3035,7 +3048,8 @@ union bpf_attr {
30353048
FN(tcp_send_ack), \
30363049
FN(send_signal_thread), \
30373050
FN(jiffies64), \
3038-
FN(read_branch_records),
3051+
FN(read_branch_records), \
3052+
FN(get_ns_current_pid_tgid),
30393053

30403054
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
30413055
* function eBPF program intends to call
@@ -3829,4 +3843,8 @@ struct bpf_sockopt {
38293843
__s32 retval;
38303844
};
38313845

3846+
struct bpf_pidns_info {
3847+
__u32 pid;
3848+
__u32 tgid;
3849+
};
38323850
#endif /* _UAPI__LINUX_BPF_H__ */

tools/testing/selftests/bpf/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ test_tcp_check_syncookie_user
3131
test_sysctl
3232
test_hashmap
3333
test_btf_dump
34+
test_current_pid_tgid_new_ns
3435
xdping
3536
test_cpp
3637
*.skel.h

tools/testing/selftests/bpf/Makefile

+2-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test
3232
test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \
3333
test_cgroup_storage \
3434
test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \
35-
test_progs-no_alu32
35+
test_progs-no_alu32 \
36+
test_current_pid_tgid_new_ns
3637

3738
# Also test bpf-gcc, if present
3839
ifneq ($(BPF_GCC),)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2020 Carlos Neira [email protected] */
3+
#include <test_progs.h>
4+
#include <sys/stat.h>
5+
#include <sys/types.h>
6+
#include <unistd.h>
7+
#include <sys/syscall.h>
8+
9+
struct bss {
10+
__u64 dev;
11+
__u64 ino;
12+
__u64 pid_tgid;
13+
__u64 user_pid_tgid;
14+
};
15+
16+
void test_ns_current_pid_tgid(void)
17+
{
18+
const char *probe_name = "raw_tracepoint/sys_enter";
19+
const char *file = "test_ns_current_pid_tgid.o";
20+
int err, key = 0, duration = 0;
21+
struct bpf_link *link = NULL;
22+
struct bpf_program *prog;
23+
struct bpf_map *bss_map;
24+
struct bpf_object *obj;
25+
struct bss bss;
26+
struct stat st;
27+
__u64 id;
28+
29+
obj = bpf_object__open_file(file, NULL);
30+
if (CHECK(IS_ERR(obj), "obj_open", "err %ld\n", PTR_ERR(obj)))
31+
return;
32+
33+
err = bpf_object__load(obj);
34+
if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
35+
goto cleanup;
36+
37+
bss_map = bpf_object__find_map_by_name(obj, "test_ns_.bss");
38+
if (CHECK(!bss_map, "find_bss_map", "failed\n"))
39+
goto cleanup;
40+
41+
prog = bpf_object__find_program_by_title(obj, probe_name);
42+
if (CHECK(!prog, "find_prog", "prog '%s' not found\n",
43+
probe_name))
44+
goto cleanup;
45+
46+
memset(&bss, 0, sizeof(bss));
47+
pid_t tid = syscall(SYS_gettid);
48+
pid_t pid = getpid();
49+
50+
id = (__u64) tid << 32 | pid;
51+
bss.user_pid_tgid = id;
52+
53+
if (CHECK_FAIL(stat("/proc/self/ns/pid", &st))) {
54+
perror("Failed to stat /proc/self/ns/pid");
55+
goto cleanup;
56+
}
57+
58+
bss.dev = st.st_dev;
59+
bss.ino = st.st_ino;
60+
61+
err = bpf_map_update_elem(bpf_map__fd(bss_map), &key, &bss, 0);
62+
if (CHECK(err, "setting_bss", "failed to set bss : %d\n", err))
63+
goto cleanup;
64+
65+
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
66+
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
67+
PTR_ERR(link))) {
68+
link = NULL;
69+
goto cleanup;
70+
}
71+
72+
/* trigger some syscalls */
73+
usleep(1);
74+
75+
err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &key, &bss);
76+
if (CHECK(err, "set_bss", "failed to get bss : %d\n", err))
77+
goto cleanup;
78+
79+
if (CHECK(id != bss.pid_tgid, "Compare user pid/tgid vs. bpf pid/tgid",
80+
"User pid/tgid %llu BPF pid/tgid %llu\n", id, bss.pid_tgid))
81+
goto cleanup;
82+
cleanup:
83+
if (!link) {
84+
bpf_link__destroy(link);
85+
link = NULL;
86+
}
87+
bpf_object__close(obj);
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2019 Carlos Neira [email protected] */
3+
4+
#include <linux/bpf.h>
5+
#include <stdint.h>
6+
#include <bpf/bpf_helpers.h>
7+
8+
static volatile struct {
9+
__u64 dev;
10+
__u64 ino;
11+
__u64 pid_tgid;
12+
__u64 user_pid_tgid;
13+
} res;
14+
15+
SEC("raw_tracepoint/sys_enter")
16+
int trace(void *ctx)
17+
{
18+
__u64 ns_pid_tgid, expected_pid;
19+
struct bpf_pidns_info nsdata;
20+
__u32 key = 0;
21+
22+
if (bpf_get_ns_current_pid_tgid(res.dev, res.ino, &nsdata,
23+
sizeof(struct bpf_pidns_info)))
24+
return 0;
25+
26+
ns_pid_tgid = (__u64)nsdata.tgid << 32 | nsdata.pid;
27+
expected_pid = res.user_pid_tgid;
28+
29+
if (expected_pid != ns_pid_tgid)
30+
return 0;
31+
32+
res.pid_tgid = ns_pid_tgid;
33+
34+
return 0;
35+
}
36+
37+
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)