Linux Modules
 help / color / mirror / Atom feed
From: Samuel Wu <wusamuel@google.com>
To: Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	 Randy Dunlap <rdunlap@infradead.org>,
	Luis Chamberlain <mcgrof@kernel.org>,
	 Petr Pavlu <petr.pavlu@suse.com>,
	Daniel Gomez <da.gomez@kernel.org>,
	 Sami Tolvanen <samitolvanen@google.com>,
	Aaron Tomlin <atomlin@atomlin.com>,
	 Alexander Viro <viro@zeniv.linux.org.uk>,
	Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
	 Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>,
	 Ihor Solodrai <ihor.solodrai@linux.dev>,
	Alexei Starovoitov <ast@kernel.org>,
	 Daniel Borkmann <daniel@iogearbox.net>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>,
	 Martin KaFai Lau <martin.lau@linux.dev>,
	Song Liu <song@kernel.org>,
	 Yonghong Song <yonghong.song@linux.dev>,
	Jiri Olsa <jolsa@kernel.org>,
	 Emil Tsalapatis <emil@etsalapatis.com>,
	Nathan Chancellor <nathan@kernel.org>,
	 Nick Desaulniers <ndesaulniers@google.com>,
	Bill Wendling <morbo@google.com>,
	 Justin Stitt <justinstitt@google.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	Petr Mladek <pmladek@suse.com>,
	 David Laight <david.laight.linux@gmail.com>,
	Samuel Wu <wusamuel@google.com>,
	 kernel-team@android.com, linux-kernel@vger.kernel.org,
	 linux-fsdevel@vger.kernel.org, linux-doc@vger.kernel.org,
	 linux-modules@vger.kernel.org, bpf@vger.kernel.org,
	 linux-kselftest@vger.kernel.org, llvm@lists.linux.dev
Subject: [PATCH bpf-next v3 7/9] selftests/proc: Add KALLSYMS_QUERY_* ioctl tests
Date: Fri, 13 Nov 2026 14:12:52 -0800	[thread overview]
Message-ID: <20261113221248.3535369-18-wusamuel@google.com> (raw)
In-Reply-To: <20261113221248.3535369-11-wusamuel@google.com>

Test KALLSYMS_QUERY_BY_NAME and KALLSYMS_QUERY_BY_ADDR against the
/proc/kallsyms text: exact and prefix names, iteration with pos and
retry after -ENAMETOOLONG, module_filter, vmlinux and module symbols,
struct size extension, and invalid input. Skip if the ioctls are
missing or addresses are hidden.

Signed-off-by: Samuel Wu <wusamuel@google.com>
---
 tools/testing/selftests/proc/.gitignore       |   1 +
 tools/testing/selftests/proc/Makefile         |   1 +
 .../selftests/proc/proc-kallsyms-query.c      | 248 ++++++++++++++++++
 3 files changed, 250 insertions(+)
 create mode 100644 tools/testing/selftests/proc/proc-kallsyms-query.c

diff --git a/tools/testing/selftests/proc/.gitignore b/tools/testing/selftests/proc/.gitignore
index 9c9735570abf..1762c28f524c 100644
--- a/tools/testing/selftests/proc/.gitignore
+++ b/tools/testing/selftests/proc/.gitignore
@@ -4,6 +4,7 @@
 /fd-003-kthread
 /proc-2-is-kthread
 /proc-fsconfig-hidepid
+/proc-kallsyms-query
 /proc-loadavg-001
 /proc-maps-race
 /proc-multiple-procfs
diff --git a/tools/testing/selftests/proc/Makefile b/tools/testing/selftests/proc/Makefile
index a7de2bb6d8be..dc3c28e6b83b 100644
--- a/tools/testing/selftests/proc/Makefile
+++ b/tools/testing/selftests/proc/Makefile
@@ -29,6 +29,7 @@ TEST_GEN_PROGS += setns-sysvipc
 TEST_GEN_PROGS += thread-self
 TEST_GEN_PROGS += proc-multiple-procfs
 TEST_GEN_PROGS += proc-fsconfig-hidepid
+TEST_GEN_PROGS += proc-kallsyms-query
 TEST_GEN_PROGS += proc-pidns
 
 include ../lib.mk
diff --git a/tools/testing/selftests/proc/proc-kallsyms-query.c b/tools/testing/selftests/proc/proc-kallsyms-query.c
new file mode 100644
index 000000000000..7788cb0f30f8
--- /dev/null
+++ b/tools/testing/selftests/proc/proc-kallsyms-query.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Tests for the KALLSYMS_QUERY_* ioctls on /proc/kallsyms */
+#undef NDEBUG
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <linux/fs.h>
+
+#include "kselftest.h"
+
+#define PTR(p) ((__u64)(uintptr_t)(p))
+#define PREFIX "vfs_"
+
+static char name_buf[512];
+static char mod_buf[64];
+
+/* Expected values, from the /proc/kallsyms text */
+static struct {
+	unsigned long long vfs_read_addr;
+	bool vfs_read_found;
+	unsigned int prefix_cnt;	/* vmlinux names with PREFIX */
+	unsigned long long mod_addr;	/* module text symbol, or 0 */
+	char mod[64];
+} ref;
+
+static void scan_text(void)
+{
+	FILE *f = fopen("/proc/kallsyms", "r");
+	unsigned long long addr;
+	char line[1024];
+	bool plain_name;
+	char name[513];
+	char mod[64];
+	bool vmlinux;
+	bool module;
+	bool text;
+	char type;
+	int n;
+
+	assert(f);
+	while (fgets(line, sizeof(line), f)) {
+		/* 3 fields for vmlinux, 4 if the line ends with [module] */
+		n = sscanf(line, "%llx %c %512s [%63[^]]]",
+			   &addr, &type, name, mod);
+		/* A longer name would be cut and parsed as vmlinux */
+		assert(n >= 3 && strlen(name) < sizeof(name) - 1);
+		vmlinux = n == 3;
+		module = n == 4 && strcmp(mod, "bpf") &&
+			 strncmp(mod, "__builtin__", strlen("__builtin__"));
+		text = type == 't' || type == 'T';
+		plain_name = name[0] != '.' && name[0] != '$';
+
+		if (vmlinux) {
+			if (!strncmp(name, PREFIX, strlen(PREFIX)))
+				ref.prefix_cnt++;
+			if (!ref.vfs_read_found && !strcmp(name, "vfs_read")) {
+				ref.vfs_read_addr = addr;
+				ref.vfs_read_found = true;
+			}
+		} else if (module && text && plain_name && !ref.mod_addr) {
+			ref.mod_addr = addr;
+			strcpy(ref.mod, mod);
+		}
+	}
+	fclose(f);
+}
+
+static struct kallsyms_query_sym bufs(size_t name_len, size_t mod_len)
+{
+	return (struct kallsyms_query_sym){
+		.sym_name = name_len ? PTR(name_buf) : 0,
+		.sym_name_len = name_len,
+		.sym_module = mod_len ? PTR(mod_buf) : 0,
+		.sym_module_len = mod_len,
+	};
+}
+
+static struct kallsyms_query_by_name name_q(const char *name, const char *mod,
+					    struct kallsyms_query_sym *sym)
+{
+	return (struct kallsyms_query_by_name){
+		.size = sizeof(struct kallsyms_query_by_name),
+		.query_name = PTR(name),
+		.module_filter = PTR(mod),
+		.sym = PTR(sym),
+		.sym_struct_size = sizeof(*sym),
+	};
+}
+
+static struct kallsyms_query_by_addr addr_q(unsigned long long addr,
+					    struct kallsyms_query_sym *sym)
+{
+	return (struct kallsyms_query_by_addr){
+		.size = sizeof(struct kallsyms_query_by_addr),
+		.query_addr = addr,
+		.sym = PTR(sym),
+		.sym_struct_size = sizeof(*sym),
+	};
+}
+
+static int by_name(int fd, void *q)
+{
+	return ioctl(fd, KALLSYMS_QUERY_BY_NAME, q) ? -errno : 0;
+}
+
+static int by_addr(int fd, void *q)
+{
+	return ioctl(fd, KALLSYMS_QUERY_BY_ADDR, q) ? -errno : 0;
+}
+
+int main(void)
+{
+	struct {
+		struct kallsyms_query_by_name q;
+		__u64 extra;
+	} qx = {};
+	struct {
+		struct kallsyms_query_sym s;
+		__u64 extra;
+	} sx = {};
+	struct kallsyms_query_by_addr qa;
+	struct kallsyms_query_by_name q;
+	struct kallsyms_query_sym sym;
+	unsigned int cnt = 0;
+	__u64 first_pos = 0;
+	char name[512];
+	__u64 size;
+	__u64 len;
+	int err;
+	int fd;
+
+	fd = open("/proc/kallsyms", O_RDONLY | O_CLOEXEC);
+	if (fd < 0)
+		ksft_exit_skip("open(/proc/kallsyms): %s\n", strerror(errno));
+	scan_text();
+	if (!ref.vfs_read_found)
+		ksft_exit_skip("vfs_read is not in /proc/kallsyms\n");
+
+	/* Exact name in vmlinux, with name and module buffers */
+	sym = bufs(sizeof(name_buf), sizeof(mod_buf));
+	q = name_q("vfs_read", "vmlinux", &sym);
+	err = by_name(fd, &q);
+	if (err == -ENOTTY)
+		ksft_exit_skip("KALLSYMS_QUERY_BY_NAME is not supported\n");
+	if (!ref.vfs_read_addr) {
+		qa = addr_q(0, &sym);
+		assert(err == -EPERM && by_addr(fd, &qa) == -EPERM);
+		ksft_exit_skip("kallsyms addresses are hidden\n");
+	}
+	assert(!err);
+	assert(sym.sym_addr == ref.vfs_read_addr && sym.sym_size > 0);
+	assert(!strcmp(name_buf, "vfs_read"));
+	assert(sym.sym_name_len == sizeof("vfs_read"));
+	assert(!mod_buf[0] && sym.sym_module_len == 1);
+	size = sym.sym_size;
+
+	/* Prefix: one match per call with pos + 1, until -ENOENT */
+	q = name_q(PREFIX, "vmlinux", &sym);
+	q.query_flags = KALLSYMS_QUERY_NAME_PREFIX;
+	sym = bufs(sizeof(name_buf), 0);
+	while (!(err = by_name(fd, &q))) {
+		assert(!strncmp(name_buf, PREFIX, strlen(PREFIX)));
+		if (!cnt++)
+			first_pos = q.pos;
+		sym = bufs(sizeof(name_buf), 0);
+		q.pos++;
+	}
+	assert(err == -ENOENT && cnt == ref.prefix_cnt);
+
+	/* -ENAMETOOLONG sets pos and both sizes; a retry succeeds */
+	sym = bufs(1, 2);
+	q.pos = 0;
+	assert(by_name(fd, &q) == -ENAMETOOLONG && q.pos == first_pos);
+	assert(sym.sym_module_len == 1);
+	len = sym.sym_name_len;
+	sym.sym_name_len = sizeof(name_buf);
+	assert(by_name(fd, &q) == 0 && q.pos == first_pos);
+	assert(sym.sym_name_len == len && strlen(name_buf) + 1 == len);
+
+	/* No match */
+	sym = bufs(0, 0);
+	q = name_q("__no_such_symbol__", NULL, &sym);
+	assert(by_name(fd, &q) == -ENOENT);
+
+	/* BY_ADDR inside vfs_read */
+	sym = bufs(sizeof(name_buf), 0);
+	qa = addr_q(ref.vfs_read_addr + 1, &sym);
+	assert(by_addr(fd, &qa) == 0 && !strcmp(name_buf, "vfs_read"));
+	assert(sym.sym_addr == ref.vfs_read_addr && sym.sym_size == size);
+
+	sym = bufs(0, 0);
+	qa = addr_q(~0ULL, &sym);
+	assert(by_addr(fd, &qa) == -ENOENT);
+
+	/* Module symbol: BY_ADDR and BY_NAME with module_filter agree */
+	if (ref.mod_addr) {
+		bool found = false;
+
+		sym = bufs(sizeof(name_buf), sizeof(mod_buf));
+		qa = addr_q(ref.mod_addr, &sym);
+		assert(by_addr(fd, &qa) == 0 && sym.sym_addr == ref.mod_addr);
+		assert(!strcmp(mod_buf, ref.mod));
+		strcpy(name, name_buf);
+		size = sym.sym_size;
+
+		q = name_q(name, ref.mod, &sym);
+		sym = bufs(0, sizeof(mod_buf));
+		while (!(err = by_name(fd, &q))) {
+			assert(!strcmp(mod_buf, ref.mod));
+			if (sym.sym_addr == ref.mod_addr) {
+				assert(sym.sym_size == size);
+				found = true;
+			}
+			sym = bufs(0, sizeof(mod_buf));
+			q.pos++;
+		}
+		assert(err == -ENOENT && found);
+	}
+
+	/* Larger structs with a zero tail are accepted */
+	qx.q = name_q("vfs_read", NULL, &sx.s);
+	qx.q.size = sizeof(qx);
+	qx.q.sym_struct_size = sizeof(sx);
+	assert(by_name(fd, &qx) == 0 && sx.s.sym_addr == ref.vfs_read_addr);
+
+	/* Unknown flags and mismatched buffer pointer/length */
+	sym = bufs(0, 0);
+	q = name_q("vfs_read", NULL, &sym);
+	q.query_flags = 1ULL << 63;
+	assert(by_name(fd, &q) == -EINVAL);
+	qa = addr_q(ref.vfs_read_addr, &sym);
+	qa.query_flags = 1;
+	assert(by_addr(fd, &qa) == -EINVAL);
+	sym = (struct kallsyms_query_sym){ .sym_name = PTR(name_buf) };
+	q = name_q("vfs_read", NULL, &sym);
+	assert(by_name(fd, &q) == -EINVAL);
+	sym = (struct kallsyms_query_sym){ .sym_module_len = sizeof(mod_buf) };
+	assert(by_name(fd, &q) == -EINVAL);
+
+	close(fd);
+	return 0;
+}
-- 
2.56.0.679.gc527c65306-goog


  parent reply	other threads:[~2026-11-13 22:14 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-13 22:12 [PATCH bpf-next v3 0/9] kallsyms: Add KALLSYMS_QUERY_BY_NAME and KALLSYMS_QUERY_BY_ADDR ioctls Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 1/9] Documentation: ioctl-number: Add PROCFS_IOCTL_MAGIC range 0x10-0x11 Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 2/9] module: Add module_kallsyms_find_name() Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 3/9] kallsyms: Add KALLSYMS_QUERY_BY_NAME ioctl to /proc/kallsyms Samuel Wu
2026-11-13 22:55   ` bot+bpf-ci
2026-11-13 22:12 ` [PATCH bpf-next v3 4/9] kallsyms: Add KALLSYMS_QUERY_BY_ADDR " Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 5/9] docs/procfs: Document KALLSYMS_QUERY_* ioctls on /proc/kallsyms Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 6/9] tools: Copy KALLSYMS_QUERY_* definitions to linux/fs.h Samuel Wu
2026-11-13 22:12 ` Samuel Wu [this message]
2026-11-13 22:27   ` [PATCH bpf-next v3 7/9] selftests/proc: Add KALLSYMS_QUERY_* ioctl tests sashiko-bot
2026-11-13 22:12 ` [PATCH bpf-next v3 8/9] libbpf: Resolve typeless ksyms via KALLSYMS_QUERY_BY_NAME ioctl Samuel Wu
2026-11-13 22:12 ` [PATCH bpf-next v3 9/9] selftests/bpf: Test typeless module ksym resolution Samuel Wu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261113221248.3535369-18-wusamuel@google.com \
    --to=wusamuel@google.com \
    --cc=akpm@linux-foundation.org \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=atomlin@atomlin.com \
    --cc=bpf@vger.kernel.org \
    --cc=brauner@kernel.org \
    --cc=corbet@lwn.net \
    --cc=da.gomez@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=david.laight.linux@gmail.com \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=ihor.solodrai@linux.dev \
    --cc=jack@suse.cz \
    --cc=jolsa@kernel.org \
    --cc=justinstitt@google.com \
    --cc=kernel-team@android.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-modules@vger.kernel.org \
    --cc=llvm@lists.linux.dev \
    --cc=martin.lau@linux.dev \
    --cc=mcgrof@kernel.org \
    --cc=memxor@gmail.com \
    --cc=morbo@google.com \
    --cc=nathan@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=petr.pavlu@suse.com \
    --cc=pmladek@suse.com \
    --cc=rdunlap@infradead.org \
    --cc=samitolvanen@google.com \
    --cc=skhan@linuxfoundation.org \
    --cc=song@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox