Trace external pointers from helpers
authorPaul Chaignon <paul.chaignon@gmail.com>
Sun, 6 Aug 2017 12:33:20 +0000 (14:33 +0200)
committerBrenden Blanco <bblanco@gmail.com>
Wed, 23 Aug 2017 05:38:51 +0000 (22:38 -0700)
At this time, a single helper can return a kernel pointer,
bpf_get_current_task.

src/cc/frontends/clang/b_frontend_action.cc
tests/python/test_clang.py
tools/cpuunclaimed.py
tools/mountsnoop.py
tools/runqlen.py

index b2f9206d34ebcf5dc407f979899b55ae432916c0..87ed228b08ecc7c3924e1e4882fb0ca2fdcf98f0 100644 (file)
@@ -77,6 +77,9 @@ class ProbeChecker : public RecursiveASTVisitor<ProbeChecker> {
   }
   bool VisitCallExpr(CallExpr *E) {
     needs_probe_ = false;
+    if (VarDecl *V = dyn_cast<VarDecl>(E->getCalleeDecl())) {
+      needs_probe_ = V->getName() == "bpf_get_current_task";
+    }
     return false;
   }
   bool VisitParenExpr(ParenExpr *E) {
index 5d9f036815df017f31bf02035a864ada7e1cc065..376a5dc59e565704646fed64552d1d7ea8f04df6 100755 (executable)
@@ -4,10 +4,11 @@
 
 from bcc import BPF
 import ctypes as ct
-from unittest import main, TestCase
+from unittest import main, skipUnless, TestCase
 import os
 import sys
 from contextlib import contextmanager
+import distutils.version
 
 @contextmanager
 def redirect_stderr(to):
@@ -21,6 +22,17 @@ def redirect_stderr(to):
             sys.stderr.flush()
             os.dup2(copied.fileno(), stderr_fd)
 
+def kernel_version_ge(major, minor):
+    # True if running kernel is >= X.Y
+    version = distutils.version.LooseVersion(os.uname()[2]).version
+    if version[0] > major:
+        return True
+    if version[0] < major:
+        return False
+    if minor and version[1] < minor:
+        return False
+    return True
+
 class TestClang(TestCase):
     def test_complex(self):
         b = BPF(src_file="test_clang_complex.c", debug=0)
@@ -454,6 +466,18 @@ int dns_test(struct __sk_buff *skb) {
         """
         b = BPF(text=text)
 
+    @skipUnless(kernel_version_ge(4,8), "requires kernel >= 4.8")
+    def test_ext_ptr_from_helper(self):
+        text = """
+#include <linux/sched.h>
+int test(struct pt_regs *ctx) {
+    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
+    return task->prio;
+}
+"""
+        b = BPF(text=text)
+        fn = b.load_func("test", BPF.KPROBE)
+
     def test_unary_operator(self):
         text = """
 #include <linux/fs.h>
index 364e80e15f956a5f8b7c695f6e4cf13b067cf9e4..58b8e4ac97c7e7152c945251d4531a1fa0150d95 100755 (executable)
@@ -144,8 +144,8 @@ int do_perf_event(struct bpf_perf_event_data *ctx)
     struct task_struct *task = NULL;
     struct cfs_rq_partial *my_q = NULL;
     task = (struct task_struct *)bpf_get_current_task();
-    bpf_probe_read(&my_q, sizeof(my_q), &task->se.cfs_rq);
-    bpf_probe_read(&len, sizeof(len), &my_q->nr_running);
+    my_q = (struct cfs_rq_partial *)task->se.cfs_rq;
+    len = my_q->nr_running;
 
     struct data_t data = {.ts = now, .cpu = cpu, .len = len};
     events.perf_submit(ctx, &data, sizeof(data));
index a7a3973a6911816867f61461330755da2b420812..223b2f8d104938ea68b6786565438979f2d1b949 100755 (executable)
@@ -104,10 +104,9 @@ int kprobe__sys_mount(struct pt_regs *ctx, char __user *source,
     bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
     event.enter.flags = flags;
     task = (struct task_struct *)bpf_get_current_task();
-    bpf_probe_read(&nsproxy, sizeof(nsproxy), &task->nsproxy);
-    bpf_probe_read(&mnt_ns, sizeof(mnt_ns), &nsproxy->mnt_ns);
-    bpf_probe_read(&event.enter.mnt_ns, sizeof(event.enter.mnt_ns),
-               &mnt_ns->ns.inum);
+    nsproxy = task->nsproxy;
+    mnt_ns = nsproxy->mnt_ns;
+    event.enter.mnt_ns = mnt_ns->ns.inum;
     events.perf_submit(ctx, &event, sizeof(event));
 
     event.type = EVENT_MOUNT_SOURCE;
@@ -160,10 +159,9 @@ int kprobe__sys_umount(struct pt_regs *ctx, char __user *target, int flags)
     bpf_get_current_comm(event.enter.comm, sizeof(event.enter.comm));
     event.enter.flags = flags;
     task = (struct task_struct *)bpf_get_current_task();
-    bpf_probe_read(&nsproxy, sizeof(nsproxy), &task->nsproxy);
-    bpf_probe_read(&mnt_ns, sizeof(mnt_ns), &nsproxy->mnt_ns);
-    bpf_probe_read(&event.enter.mnt_ns, sizeof(event.enter.mnt_ns),
-               &mnt_ns->ns.inum);
+    nsproxy = task->nsproxy;
+    mnt_ns = nsproxy->mnt_ns;
+    event.enter.mnt_ns = mnt_ns->ns.inum;
     events.perf_submit(ctx, &event, sizeof(event));
 
     event.type = EVENT_UMOUNT_TARGET;
index 4c8a095221159d92a0380c7855026a36ba5af8a2..4a6bc48a681842323bbb7f0c6b5d01ba0abdf0aa 100755 (executable)
@@ -81,8 +81,8 @@ int do_perf_event()
     // of BPF will support task_rq(p) or something similar as a more reliable
     // interface.
     task = (struct task_struct *)bpf_get_current_task();
-    bpf_probe_read(&my_q, sizeof(my_q), &task->se.cfs_rq);
-    bpf_probe_read(&len, sizeof(len), &my_q->nr_running);
+    my_q = (struct cfs_rq_partial *)task->se.cfs_rq;
+    len = my_q->nr_running;
 
     // Calculate run queue length by subtracting the currently running task,
     // if present. len 0 == idle, len 1 == one running task.