Re: [PATCH v2] seccomp: passthrough uretprobe systemcall without filtering

From: Jiri Olsa
Date: Sun Feb 02 2025 - 06:08:35 EST


On Thu, Jan 30, 2025 at 10:53:47PM +0100, Jiri Olsa wrote:

SNIP

> > > > I think this would mean that this test suite would need to run as
> > > > privileged. Is that Ok? or maybe it'd be better to have a new suite?
> > > >
> > > > > With at least these cases combinations below. Check each of:
> > > > >
> > > > > - not using uretprobe passes
> > > > > - using uretprobe passes (and validates that uretprobe did work)
> > > > >
> > > > > in each of the following conditions:
> > > > >
> > > > > - default-allow filter
> > > > > - default-block filter
> > > > > - filter explicitly blocking __NR_uretprobe and nothing else
> > > > > - filter explicitly allowing __NR_uretprobe (and only other
> > > > > required syscalls)
> > > >
> > > > Ok.
> > >
> > > please let me know if I can help in any way with tests
> >
> > Thanks! Is there a way to partition this work? I'd appreciate the help
> > if we can find some way of doing so.
>
> sure, I'll check the seccomp selftests and let you know

hi,
if it's any help, feel free to use the code below that creates uretprobe,
it could be bit simpler if we use libbpf, but I think that's not an option

jirka


---
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 8c3a73461475..1f99d31d05a1 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -47,6 +47,7 @@
#include <linux/kcmp.h>
#include <sys/resource.h>
#include <sys/capability.h>
+#include <linux/perf_event.h>

#include <unistd.h>
#include <sys/syscall.h>
@@ -4888,6 +4889,130 @@ TEST(tsync_vs_dead_thread_leader)
EXPECT_EQ(0, status);
}

+__attribute__((noinline)) int probed(void)
+{
+ return 1;
+}
+
+static int parse_uint_from_file(const char *file, const char *fmt)
+{
+ int err = -1, ret;
+ FILE *f;
+
+ f = fopen(file, "re");
+ if (f) {
+ err = fscanf(f, fmt, &ret);
+ fclose(f);
+ }
+ return err == 1 ? ret : err;
+}
+
+static int determine_uprobe_perf_type(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/type";
+
+ return parse_uint_from_file(file, "%d\n");
+}
+
+static int determine_uprobe_retprobe_bit(void)
+{
+ const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
+
+ return parse_uint_from_file(file, "config:%d\n");
+}
+
+static ssize_t get_uprobe_offset(const void *addr)
+{
+ size_t start, base, end;
+ bool found = false;
+ char buf[256];
+ FILE *f;
+
+ f = fopen("/proc/self/maps", "r");
+ if (!f)
+ return -1;
+
+ while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
+ if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
+ found = true;
+ break;
+ }
+ }
+ fclose(f);
+ return found ? (uintptr_t)addr - start + base : -1;
+}
+
+static int create_uretprobe(void *addr)
+{
+ const size_t attr_sz = sizeof(struct perf_event_attr);
+ struct perf_event_attr attr;
+ ssize_t offset;
+ int type, bit;
+
+ memset(&attr, 0, attr_sz);
+
+ type = determine_uprobe_perf_type();
+ if (type < 0)
+ return -1;
+ bit = determine_uprobe_retprobe_bit();
+ if (bit < 0)
+ return -1;
+ offset = get_uprobe_offset(probed);
+ if (offset < 0)
+ return -1;
+
+ attr.config |= 1 << bit;
+ attr.size = attr_sz;
+ attr.type = type;
+ attr.config1 = ptr_to_u64("/proc/self/exe");
+ attr.config2 = offset;
+
+ return syscall(__NR_perf_event_open, &attr,
+ getpid() /* pid */, -1 /* cpu */, -1 /* group_fd */,
+ PERF_FLAG_FD_CLOEXEC);
+}
+
+TEST(uretprobe)
+{
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit_group, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)ARRAY_SIZE(filter),
+ .filter = filter,
+ };
+ long ret;
+ int fd;
+
+ fd = create_uretprobe(probed);
+ ASSERT_GE(fd, 0) {
+ TH_LOG("Failed to create uretprobe!!");
+ }
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
+ ASSERT_NE(ENOSYS, errno) {
+ TH_LOG("Kernel does not support seccomp syscall!");
+ }
+ EXPECT_EQ(0, ret) {
+ TH_LOG("Could not install filter!");
+ }
+
+ /* should not explode */
+ probed();
+
+ /* we could call close(fd), but we'd need extra filter for
+ * that and since we we are calling _exit right away.. */
+}
+
/*
* TODO:
* - expand NNP testing