Thread (15 messages) 15 messages, 5 authors, 2025-02-02

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

From: Jiri Olsa <hidden>
Date: 2025-02-02 11:08:20
Also in: bpf, linux-trace-kernel, lkml, stable
Subsystem: kernel selftest framework, secure computing, the rest · Maintainers: Shuah Khan, Kees Cook, Linus Torvalds

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

SNIP
quoted
quoted
quoted
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?
quoted
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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help