Skip to content

Commit

Permalink
Remove namespace code from libbpf to fix USDT
Browse files Browse the repository at this point in the history
This removes the namespace code from libbpf, as they are no longer necessary
since #2324 was merged, and have caused regressions on older Kernels that do
not use the new API for creating probes. This also deletes the dead code for
namespace handling in libbpf, as this was the last use of it.

This also introduces regression tests to ensure that processes in containers
can be USDT probed, by adding tests that unshare the mount and process
namespaces.
  • Loading branch information
dalehamel authored and yonghong-song committed Jan 21, 2020
1 parent da59f37 commit c6a3f02
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 68 deletions.
69 changes: 1 addition & 68 deletions src/cc/libbpf.c
Expand Up @@ -880,68 +880,6 @@ static int bpf_attach_tracing_event(int progfd, const char *event_path, int pid,
return 0;
}

static int enter_mount_ns(int pid) {
struct stat self_stat, target_stat;
int self_fd = -1, target_fd = -1;
char buf[64];

if (pid < 0)
return -1;

if ((size_t)snprintf(buf, sizeof(buf), "/proc/%d/ns/mnt", pid) >= sizeof(buf))
return -1;

self_fd = open("/proc/self/ns/mnt", O_RDONLY);
if (self_fd < 0) {
perror("open(/proc/self/ns/mnt)");
return -1;
}

target_fd = open(buf, O_RDONLY);
if (target_fd < 0) {
perror("open(/proc/<pid>/ns/mnt)");
goto error;
}

if (fstat(self_fd, &self_stat)) {
perror("fstat(self_fd)");
goto error;
}

if (fstat(target_fd, &target_stat)) {
perror("fstat(target_fd)");
goto error;
}

// both target and current ns are same, avoid setns and close all fds
if (self_stat.st_ino == target_stat.st_ino)
goto error;

if (setns(target_fd, CLONE_NEWNS)) {
perror("setns(target)");
goto error;
}

close(target_fd);
return self_fd;

error:
if (self_fd >= 0)
close(self_fd);
if (target_fd >= 0)
close(target_fd);
return -1;
}

static void exit_mount_ns(int fd) {
if (fd < 0)
return;

if (setns(fd, CLONE_NEWNS))
perror("setns");
close(fd);
}

/* Creates an [uk]probe using debugfs.
* On success, the path to the probe is placed in buf (which is assumed to be of size PATH_MAX).
*/
Expand All @@ -950,7 +888,7 @@ static int create_probe_event(char *buf, const char *ev_name,
const char *config1, uint64_t offset,
const char *event_type, pid_t pid, int maxactive)
{
int kfd = -1, res = -1, ns_fd = -1;
int kfd = -1, res = -1;
char ev_alias[256];
bool is_kprobe = strncmp("kprobe", event_type, 6) == 0;

Expand Down Expand Up @@ -988,7 +926,6 @@ static int create_probe_event(char *buf, const char *ev_name,
close(kfd);
return -1;
}
ns_fd = enter_mount_ns(pid);
}

if (write(kfd, buf, strlen(buf)) < 0) {
Expand All @@ -1000,14 +937,10 @@ static int create_probe_event(char *buf, const char *ev_name,
goto error;
}
close(kfd);
if (!is_kprobe)
exit_mount_ns(ns_fd);
snprintf(buf, PATH_MAX, "/sys/kernel/debug/tracing/events/%ss/%s",
event_type, ev_alias);
return 0;
error:
if (!is_kprobe)
exit_mount_ns(ns_fd);
return -1;
}

Expand Down
68 changes: 68 additions & 0 deletions tests/cc/test_usdt_probes.cc
Expand Up @@ -184,6 +184,21 @@ static int probe_num_arguments(const char *bin_path, const char *func_name) {
return num_arguments;
}

// Unsharing pid namespace requires forking
// this uses pgrep to find the child process, by searching for a process
// that has the unshare as its parent
static int unshared_child_pid(const int ppid) {
int child_pid;
char cmd[512];
const char *cmdfmt = "pgrep -P %d";

sprintf(cmd, cmdfmt, ppid);
if (cmd_scanf(cmd, "%d", &child_pid) != 0) {
return -1;
}
return child_pid;
}

TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
size_t mri_probe_count = 0;

Expand Down Expand Up @@ -293,3 +308,56 @@ TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
}
}
}

// These tests are expected to fail if there is no Ruby with dtrace probes
TEST_CASE("test probing running Ruby process in namespaces",
"[usdt][!mayfail]") {
SECTION("in separate mount namespace") {
static char _unshare[] = "unshare";
const char *const argv[4] = {_unshare, "--mount", "ruby", NULL};

ChildProcess unshare(argv[0], (char **const)argv);
if (!unshare.spawned())
return;
int ruby_pid = unshare.pid();

ebpf::BPF bpf;
ebpf::USDT u(ruby_pid, "ruby", "gc__mark__begin", "on_event");
u.set_probe_matching_kludge(1); // Also required for overlayfs...

auto res = bpf.init("int on_event() { return 0; }", {}, {u});
REQUIRE(res.msg() == "");
REQUIRE(res.code() == 0);

res = bpf.attach_usdt(u, ruby_pid);
REQUIRE(res.code() == 0);

res = bpf.detach_usdt(u, ruby_pid);
REQUIRE(res.code() == 0);
}

SECTION("in separate mount namespace and separate PID namespace") {
static char _unshare[] = "unshare";
const char *const argv[7] = {_unshare, "--fork", "--mount", "--pid",
"--mount-proc", "ruby", NULL};

ChildProcess unshare(argv[0], (char **const)argv);
if (!unshare.spawned())
return;
int ruby_pid = unshared_child_pid(unshare.pid());

ebpf::BPF bpf;
ebpf::USDT u(ruby_pid, "ruby", "gc__mark__begin", "on_event");
u.set_probe_matching_kludge(1); // Also required for overlayfs...

auto res = bpf.init("int on_event() { return 0; }", {}, {u});
REQUIRE(res.msg() == "");
REQUIRE(res.code() == 0);

res = bpf.attach_usdt(u, ruby_pid);
REQUIRE(res.code() == 0);

res = bpf.detach_usdt(u, ruby_pid);
REQUIRE(res.code() == 0);
}
}

0 comments on commit c6a3f02

Please sign in to comment.