From: Carlos Neira <hidden> Date: 2019-10-17 15:00:44
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.
Changes from V13:
- refactored selftests
- refactored ebpf helper
Signed-off-by: Carlos Neira <redacted>
Carlos Neira (5):
fs/nsfs.c: added ns_match
bpf: added new helper bpf_get_ns_current_pid_tgid
tools: Added bpf_get_ns_current_pid_tgid helper
tools/testing/selftests/bpf: Add self-tests for new helper.
bpf_helpers_doc.py: Add struct bpf_pidns_info to known types
fs/nsfs.c | 8 ++
include/linux/bpf.h | 1 +
include/linux/proc_ns.h | 2 +
include/uapi/linux/bpf.h | 20 +++-
kernel/bpf/core.c | 1 +
kernel/bpf/helpers.c | 45 +++++++++
kernel/trace/bpf_trace.c | 2 +
scripts/bpf_helpers_doc.py | 1 +
tools/include/uapi/linux/bpf.h | 20 +++-
.../bpf/prog_tests/get_ns_current_pid_tgid.c | 96 +++++++++++++++++++
.../bpf/progs/get_ns_current_pid_tgid_kern.c | 53 ++++++++++
11 files changed, 247 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/get_ns_current_pid_tgid.c
create mode 100644 tools/testing/selftests/bpf/progs/get_ns_current_pid_tgid_kern.c
--
2.20.1
From: Carlos Neira <hidden> Date: 2019-10-17 15:00:49
ns_match returns true if the namespace inode and dev_t matches the ones
provided by the caller.
Signed-off-by: Carlos Neira <redacted>
---
fs/nsfs.c | 8 ++++++++
include/linux/proc_ns.h | 2 ++
2 files changed, 10 insertions(+)
From: Carlos Neira <hidden> Date: 2019-10-17 15:00:49
New bpf helper bpf_get_ns_current_pid_tgid,
This helper will return pid and tgid from current task
which namespace matches dev_t and inode number provided,
this will allows us to instrument a process inside a container.
Signed-off-by: Carlos Neira <redacted>
---
include/linux/bpf.h | 1 +
include/uapi/linux/bpf.h | 20 +++++++++++++++++-
kernel/bpf/core.c | 1 +
kernel/bpf/helpers.c | 45 ++++++++++++++++++++++++++++++++++++++++
kernel/trace/bpf_trace.c | 2 ++
5 files changed, 68 insertions(+), 1 deletion(-)
@@ -2750,6 +2750,19 @@ union bpf_attr {***-EOPNOTSUPP**kernelconfigurationdoesnotenableSYNcookies****-EPROTONOSUPPORT**IPpacketversionisnot4or6+*+*u64bpf_get_ns_current_pid_tgid(u64dev,u64ino,structbpf_pidns_info*nsdata,u32size)+*Description+*Returns0onsuccess,valuesfor*pid*and*tgid*asseenfromthecurrent+**namespace*willbereturnedin*nsdata*.+*+*Onfailure,thereturnedvalueisoneofthefollowing:+*+***-EINVAL**ifdevandinumsupplieddon'tmatchdev_tandinodenumber+*withnsfsofcurrenttask,orifdevconversiontodev_tlosthighbits.+*+***-ENOENT**if/proc/self/nsdoesnotexists.+**/#define __BPF_FUNC_MAPPER(FN) \FN(unspec),\
@@ -2862,7 +2875,8 @@ union bpf_attr {FN(sk_storage_get),\FN(sk_storage_delete),\FN(send_signal),\-FN(tcp_gen_syncookie),+FN(tcp_gen_syncookie),\+FN(get_ns_current_pid_tgid),/* integer value in 'imm' field of BPF_CALL instruction selects which helper*functioneBPFprogramintendstocall
@@ -2750,6 +2750,19 @@ union bpf_attr {***-EOPNOTSUPP**kernelconfigurationdoesnotenableSYNcookies****-EPROTONOSUPPORT**IPpacketversionisnot4or6+*+*u64bpf_get_ns_current_pid_tgid(u64dev,u64ino,structbpf_pidns_info*nsdata,u32size)+*Description+*Returns0onsuccess,valuesfor*pid*and*tgid*asseenfromthecurrent+**namespace*willbereturnedin*nsdata*.+*+*Onfailure,thereturnedvalueisoneofthefollowing:+*+***-EINVAL**ifdevandinumsupplieddon'tmatchdev_tandinodenumber+*withnsfsofcurrenttask,orifdevconversiontodev_tlosthighbits.+*+***-ENOENT**if/proc/self/nsdoesnotexists.+**/#define __BPF_FUNC_MAPPER(FN) \FN(unspec),\
@@ -2862,7 +2875,8 @@ union bpf_attr {FN(sk_storage_get),\FN(sk_storage_delete),\FN(send_signal),\-FN(tcp_gen_syncookie),+FN(tcp_gen_syncookie),\+FN(get_ns_current_pid_tgid),/* integer value in 'imm' field of BPF_CALL instruction selects which helper*functioneBPFprogramintendstocall
@@ -0,0 +1,96 @@+// SPDX-License-Identifier: GPL-2.0+/* Copyright (c) 2019 Carlos Neira cneirabustos@gmail.com */+#include<test_progs.h>+#include<sys/stat.h>+#include<sys/types.h>+#include<sys/stat.h>+#include<unistd.h>+#include<sys/syscall.h>++structbss{+__u64dev;+__u64ino;+__u64pidtgid;+__u64userpidtgid;+}data;++voidtest_get_ns_current_pid_tgid(void)+{+constchar*probe_name="raw_tracepoint/sys_enter";+constchar*file="get_ns_current_pid_tgid_kern.o";+structbpf_object_load_attrload_attr={};+structbpf_link*link=NULL;+structbpf_program*prog;+structbpf_map*bss_map;+structbpf_object*obj;+interr,duration=0;+const__u32key=0;+structstatst;+__u64id;++obj=bpf_object__open(file);+if(CHECK(IS_ERR_OR_NULL(obj),"obj_open",+"failed to open '%s': %ld\n",+file,PTR_ERR(obj)))+gotocleanup;++prog=bpf_object__find_program_by_title(obj,probe_name);+if(CHECK(!prog,"find_probe",+"prog '%s' not found\n",probe_name))+gotocleanup;++bpf_program__set_type(prog,BPF_PROG_TYPE_RAW_TRACEPOINT);++load_attr.obj=obj;+load_attr.log_level=0;+load_attr.target_btf_path=NULL;+err=bpf_object__load_xattr(&load_attr);+if(CHECK(err,"obj_load",+"failed to load prog '%s': %d\n",+probe_name,err))+gotocleanup;++link=bpf_program__attach_raw_tracepoint(prog,"sys_enter");+if(CHECK(IS_ERR(link),"attach_raw_tp","err %ld\n",+PTR_ERR(link)))+gotocleanup;++bss_map=bpf_object__find_map_by_name(obj,"ns_data_map");+if(CHECK(!bss_map,"find_bss_map","failed\n"))+gotocleanup;++memset(&data,0,sizeof(data));+pid_ttid=syscall(SYS_gettid);+pid_tpid=getpid();++id=(__u64)tid<<32|pid;+data.userpidtgid=id;++if(CHECK(stat("/proc/self/ns/pid",&st),"stat","failed\n"))+gotocleanup;++data.dev=st.st_dev;+data.ino=st.st_ino;++err=bpf_map_update_elem(bpf_map__fd(bss_map),&key,&data,0);+if(CHECK(err,"setting_bss","failed to set bss data: %d\n",err))+gotocleanup;++/* trigger some syscalls */+usleep(1);++err=bpf_map_lookup_elem(bpf_map__fd(bss_map),&key,&data);+if(CHECK(err,"set_bss","failed to get bss data: %d\n",err))+gotocleanup;++if(CHECK(id!=data.pidtgid,"Compare user pid/tgid vs. bpf pid/tgid",+"User pid/tgid %llu EBPF pid/tgid %llu\n",id,data.pidtgid))+gotocleanup;+cleanup:++if(!IS_ERR_OR_NULL(link)){+bpf_link__destroy(link);+link=NULL;+}+bpf_object__close(obj);+}
From: Simon Horman <hidden> Date: 2019-10-18 09:24:53
On Thu, Oct 17, 2019 at 12:00:28PM -0300, Carlos Neira wrote:
quoted hunk
ns_match returns true if the namespace inode and dev_t matches the ones
provided by the caller.
Signed-off-by: Carlos Neira <redacted>
---
fs/nsfs.c | 8 ++++++++
include/linux/proc_ns.h | 2 ++
2 files changed, 10 insertions(+)
From: Yonghong Song <hidden> Date: 2019-10-18 17:10:50
On 10/17/19 8:00 AM, Carlos Neira wrote:
New bpf helper bpf_get_ns_current_pid_tgid,
This helper will return pid and tgid from current task
which namespace matches dev_t and inode number provided,
this will allows us to instrument a process inside a container.
Signed-off-by: Carlos Neira <redacted>
You need to rebase the whole series, a new helper skb_output
is just added.
@@ -2750,6 +2750,19 @@ union bpf_attr {***-EOPNOTSUPP**kernelconfigurationdoesnotenableSYNcookies****-EPROTONOSUPPORT**IPpacketversionisnot4or6+*+*u64bpf_get_ns_current_pid_tgid(u64dev,u64ino,structbpf_pidns_info*nsdata,u32size)
-EINVAL/-ENOENT may be returned, so let us have return type "int" instead.
+ * Description
+ * Returns 0 on success, values for *pid* and *tgid* as seen from the current
+ * *namespace* will be returned in *nsdata*.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** if dev and inum supplied don't match dev_t and inode number
+ * with nsfs of current task, or if dev conversion to dev_t lost high bits.
+ *
+ * **-ENOENT** if /proc/self/ns does not exists.
Let us do not hard code the /proc/self/ns path. Just mention that the
pidns does not exist for the current task.
@@ -2862,7 +2875,8 @@ union bpf_attr { FN(sk_storage_get), \ FN(sk_storage_delete), \ FN(send_signal), \- FN(tcp_gen_syncookie),+ FN(tcp_gen_syncookie), \+ FN(get_ns_current_pid_tgid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
@@ -2862,7 +2875,8 @@ union bpf_attr { FN(sk_storage_get), \ FN(sk_storage_delete), \ FN(send_signal), \- FN(tcp_gen_syncookie),+ FN(tcp_gen_syncookie), \+ FN(get_ns_current_pid_tgid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call
Your load_attr only has 'obj', you could use bpf_object__load
for simplicity.
+
+ link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
+ if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
+ PTR_ERR(link)))
+ goto cleanup;
+
+ bss_map = bpf_object__find_map_by_name(obj, "ns_data_map");
+ if (CHECK(!bss_map, "find_bss_map", "failed\n"))
+ goto cleanup;
+
+ memset(&data, 0, sizeof(data));
+ pid_t tid = syscall(SYS_gettid);
+ pid_t pid = getpid();
+
+ id = (__u64) tid << 32 | pid;
+ data.userpidtgid = id;
+
+ if (CHECK(stat("/proc/self/ns/pid", &st), "stat","failed\n"))
+ goto cleanup;
+
+ data.dev = st.st_dev;
+ data.ino = st.st_ino;
+
+ err = bpf_map_update_elem(bpf_map__fd(bss_map), &key, &data, 0);
+ if (CHECK(err, "setting_bss", "failed to set bss data: %d\n", err))
+ goto cleanup;
Typically, we would like to do map_update_elem first and then
do attach_raw_tracepoint. This will ensure updated elem is seen
even for the first invocation of the program.
In your case, since you ignore all unmatched version, so
I won't insist if there is no revision needed.
Since you need respin any way, I suggest to switch the
order between bpf_map_update_elem and attach_raw_tracepoint, which is a
good practice any way.
quoted hunk
+
+ /* trigger some syscalls */
+ usleep(1);
+
+ err = bpf_map_lookup_elem(bpf_map__fd(bss_map), &key, &data);
+ if (CHECK(err, "set_bss", "failed to get bss data: %d\n", err))
+ goto cleanup;
+
+ if (CHECK(id != data.pidtgid, "Compare user pid/tgid vs. bpf pid/tgid",
+ "User pid/tgid %llu EBPF pid/tgid %llu\n", id, data.pidtgid))
+ goto cleanup;
+cleanup:
+
+ if (!IS_ERR_OR_NULL(link)) {
+ bpf_link__destroy(link);
+ link = NULL;
+ }
+ bpf_object__close(obj);
+}
In you code, you use ns_data_map which has max_entries = 1.
In this case, static volatile variable can be used to
simplify the bpf program. You have
'static struct res data' which will turn into a map
as well. So now you have two maps to hold the nsdata.
I suggest to remove the above ns_data_map.
You can take a look at the below commit for an example:
commit 666b2c10ee9d51f14d04c416a14b1cb6fd0846e4
Author: Andrii Nakryiko [off-list ref]
Date: Wed Oct 9 13:14:58 2019 -0700
selftests/bpf: Add read-only map values propagation tests
Add tests checking that verifier does proper constant propagation for
read-only maps. If constant propagation didn't work, skipp_loop and
part_loop BPF programs would be rejected due to BPF verifier otherwise
not being able to prove they ever complete. With constant propagation,
though, they are succesfully validated as properly terminating loops.
On Sat, Oct 19, 2019 at 1:58 AM Yonghong Song [off-list ref] wrote:
On 10/17/19 8:00 AM, Carlos Neira wrote:
quoted
Self tests added for new helper
Signed-off-by: Carlos Neira <redacted>
---
.../bpf/prog_tests/get_ns_current_pid_tgid.c | 96 +++++++++++++++++++
.../bpf/progs/get_ns_current_pid_tgid_kern.c | 53 ++++++++++
It looks like typical naming convention is:
prog_test/<something>.c
progs/test_<something>.c
Let's keep this consistent. I'm about to do a bit smarter Makefile
that will capture this convention, so it's good to have less exception
to create. Thanks!
Otherwise, besides what Yonghong mentioned, this look good to me.
From: Carlos Antonio Neira Bustos <hidden> Date: 2019-10-21 19:14:58
On Mon, Oct 21, 2019 at 11:20:01AM -0700, Andrii Nakryiko wrote:
On Sat, Oct 19, 2019 at 1:58 AM Yonghong Song [off-list ref] wrote:
quoted
On 10/17/19 8:00 AM, Carlos Neira wrote:
quoted
Self tests added for new helper
Signed-off-by: Carlos Neira <redacted>
---
.../bpf/prog_tests/get_ns_current_pid_tgid.c | 96 +++++++++++++++++++
.../bpf/progs/get_ns_current_pid_tgid_kern.c | 53 ++++++++++
It looks like typical naming convention is:
prog_test/<something>.c
progs/test_<something>.c
Let's keep this consistent. I'm about to do a bit smarter Makefile
that will capture this convention, so it's good to have less exception
to create. Thanks!
Otherwise, besides what Yonghong mentioned, this look good to me.
Thanks Andrii,
I have a doubt, I don't find in prog_tests/rdonly_map.c where is "test_rdo.bss" defined ?, is called in line 43 but I'm missing how to is it used as I don't see it defined.
Bests
On Mon, Oct 21, 2019 at 12:14 PM Carlos Antonio Neira Bustos
[off-list ref] wrote:
On Mon, Oct 21, 2019 at 11:20:01AM -0700, Andrii Nakryiko wrote:
quoted
On Sat, Oct 19, 2019 at 1:58 AM Yonghong Song [off-list ref] wrote:
quoted
On 10/17/19 8:00 AM, Carlos Neira wrote:
quoted
Self tests added for new helper
Signed-off-by: Carlos Neira <redacted>
---
.../bpf/prog_tests/get_ns_current_pid_tgid.c | 96 +++++++++++++++++++
.../bpf/progs/get_ns_current_pid_tgid_kern.c | 53 ++++++++++
It looks like typical naming convention is:
prog_test/<something>.c
progs/test_<something>.c
Let's keep this consistent. I'm about to do a bit smarter Makefile
that will capture this convention, so it's good to have less exception
to create. Thanks!
Otherwise, besides what Yonghong mentioned, this look good to me.
Thanks Andrii,
I have a doubt, I don't find in prog_tests/rdonly_map.c where is "test_rdo.bss" defined ?, is called in line 43 but I'm missing how to is it used as I don't see it defined.
This map is created by libbpf implicitly from global variables used by
BPF object. You just look it up by name, set its value to whatever you
need global variables to be set up to, and that value will be
available to BPF program. From BPF program side, when you update
global variable, that value can be read from user space using that
same test_rdo.bss map. Does it make sense?
From: Carlos Antonio Neira Bustos <hidden> Date: 2019-10-22 16:50:43
On Mon, Oct 21, 2019 at 12:18:33PM -0700, Andrii Nakryiko wrote:
On Mon, Oct 21, 2019 at 12:14 PM Carlos Antonio Neira Bustos
[off-list ref] wrote:
quoted
On Mon, Oct 21, 2019 at 11:20:01AM -0700, Andrii Nakryiko wrote:
quoted
On Sat, Oct 19, 2019 at 1:58 AM Yonghong Song [off-list ref] wrote:
quoted
On 10/17/19 8:00 AM, Carlos Neira wrote:
quoted
Self tests added for new helper
Signed-off-by: Carlos Neira <redacted>
---
.../bpf/prog_tests/get_ns_current_pid_tgid.c | 96 +++++++++++++++++++
.../bpf/progs/get_ns_current_pid_tgid_kern.c | 53 ++++++++++
It looks like typical naming convention is:
prog_test/<something>.c
progs/test_<something>.c
Let's keep this consistent. I'm about to do a bit smarter Makefile
that will capture this convention, so it's good to have less exception
to create. Thanks!
Otherwise, besides what Yonghong mentioned, this look good to me.
Thanks Andrii,
I have a doubt, I don't find in prog_tests/rdonly_map.c where is "test_rdo.bss" defined ?, is called in line 43 but I'm missing how to is it used as I don't see it defined.
This map is created by libbpf implicitly from global variables used by
BPF object. You just look it up by name, set its value to whatever you
need global variables to be set up to, and that value will be
available to BPF program. From BPF program side, when you update
global variable, that value can be read from user space using that
same test_rdo.bss map. Does it make sense?