aboutsummaryrefslogtreecommitdiffstats
path: root/tools/fops_probe
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-12-03 11:34:27 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-12-03 18:48:14 +0100
commitd20ee9bd98f1a0e98c2d02da825acf3628562e8d (patch)
tree6f4cfeb81e6a6beff7ffc87e6b55ae489f77d1bf /tools/fops_probe
parenta715d760ae486407f27f62815428da011d06e8dd (diff)
tools: add fops_probe utility
fops_probe utility helps to understand what file_operations callbacks are attached to a particular file. Requries KCOV and KALLSYMS. Build with: g++ tools/fops_probe/fops_probe.cc -Wall -static -o fops_probe Then copy the binary to target machine and run as: ./fops_probe /dev/fb0 You should see output similar to: ffffffff81bcccb9 vfs_read ................ ffffffff83af85c3 fb_read ffffffff83b52af5 cirrusfb_sync ffffffff81bcd219 vfs_write ................ ffffffff83af7fe2 fb_write ffffffff83b52af5 cirrusfb_sync ffffffff81c1b745 do_vfs_ioctl ffffffff83af7ea9 fb_ioctl ffffffff81a4ea44 do_mmap ................ ffffffff83af716c fb_mmap which allows to understand what callbacks are associated with /dev/fb0.
Diffstat (limited to 'tools/fops_probe')
-rw-r--r--tools/fops_probe/fops_probe.cc170
1 files changed, 170 insertions, 0 deletions
diff --git a/tools/fops_probe/fops_probe.cc b/tools/fops_probe/fops_probe.cc
new file mode 100644
index 000000000..c3c3332a6
--- /dev/null
+++ b/tools/fops_probe/fops_probe.cc
@@ -0,0 +1,170 @@
+// Copyright 2019 syzkaller project authors. All rights reserved.
+// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
+
+// fops_probe utility helps to understand what file_operations callbacks
+// are attached to a particular file. Requries KCOV and KALLSYMS.
+// Build with:
+// g++ tools/fops_probe/fops_probe.cc -Wall -static -o fops_probe
+// Then copy the binary to target machine and run as:
+// ./fops_probe /dev/fb0
+// You should see output similar to:
+//
+// ffffffff81bcccb9 vfs_read
+// ................
+// ffffffff83af85c3 fb_read
+// ffffffff83b52af5 cirrusfb_sync
+//
+// ffffffff81bcd219 vfs_write
+// ................
+// ffffffff83af7fe2 fb_write
+// ffffffff83b52af5 cirrusfb_sync
+//
+// ffffffff81c1b745 do_vfs_ioctl
+// ffffffff83af7ea9 fb_ioctl
+//
+// ffffffff81a4ea44 do_mmap
+// ................
+// ffffffff83af716c fb_mmap
+//
+// which allows to understand what callbacks are associated with /dev/fb0.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/kcov.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <map>
+#include <set>
+#include <string>
+
+#define COVER_SIZE (1 << 20)
+
+typedef std::map<long long, std::string> kallsyms_map_t;
+
+static __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void failf(const char* msg, ...);
+static kallsyms_map_t read_kallsyms();
+static bool is_blacklisted(const std::string& sym);
+static void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
+ const std::string& start_sym, std::function<void(void)> fn);
+
+int main(int argc, char** argv)
+{
+ if (argc != 2)
+ failf("usage: fops_probe file");
+ int fd = open(argv[1], O_RDWR);
+ if (fd == -1) {
+ fd = open(argv[1], O_RDONLY);
+ if (fd == -1)
+ failf("failed to open %s", argv[1]);
+ }
+ const kallsyms_map_t kallsyms = read_kallsyms();
+ int kcov = open("/sys/kernel/debug/kcov", O_RDWR);
+ if (kcov == -1)
+ failf("failed to open /sys/kernel/debug/kcov");
+ if (ioctl(kcov, KCOV_INIT_TRACE, COVER_SIZE))
+ failf("KCOV_INIT_TRACE failed");
+ uint64_t* cover = (uint64_t*)mmap(NULL, COVER_SIZE * 8, PROT_READ | PROT_WRITE, MAP_SHARED, kcov, 0);
+ if (cover == MAP_FAILED)
+ failf("cover mmap failed");
+ if (ioctl(kcov, KCOV_ENABLE, KCOV_TRACE_PC))
+ failf("KCOV_ENABLE failed");
+ probe_callback(cover, kallsyms, "do_vfs_ioctl", [&]() { ioctl(fd, 0, 0); });
+ probe_callback(cover, kallsyms, "do_mmap", [&]() { mmap(0, 4096, PROT_READ, MAP_PRIVATE, fd, 0); });
+ probe_callback(cover, kallsyms, "vfs_write", [&]() { write(fd, 0, 0); });
+ probe_callback(cover, kallsyms, "vfs_read", [&]() { read(fd, 0, 0); });
+ return 0;
+}
+
+void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
+ const std::string& start_sym, std::function<void(void)> fn)
+{
+ __atomic_store_n(&cover[0], 0, __ATOMIC_SEQ_CST);
+ fn();
+ uint64_t ncover = __atomic_load_n(&cover[0], __ATOMIC_SEQ_CST);
+ bool started = false;
+ std::set<std::string> seen;
+ for (uint64_t i = 0; i < ncover; i++) {
+ long long pc = cover[i + 1];
+ auto it = kallsyms.lower_bound(pc - 1);
+ const std::string& sym = it == kallsyms.begin() ? "" : (--it)->second;
+ if (!started && sym != start_sym)
+ continue;
+ started = true;
+ if (!seen.insert(sym).second || is_blacklisted(sym))
+ continue;
+ printf("%0llx %s\n", pc, sym.c_str());
+ }
+ printf("\n");
+}
+
+bool is_blacklisted(const std::string& sym)
+{
+ static const char* blacklist[] = {
+ "security",
+ "tomoyo",
+ "selinux",
+ "apparmor",
+ "smack",
+ "policy",
+ "stack_trace",
+ "should_fail",
+ "debug",
+ "trace",
+ "snprintf",
+ "vsnprintf",
+ };
+ for (size_t i = 0; i < sizeof(blacklist) / sizeof(blacklist[0]); i++) {
+ if (!strncmp(sym.c_str(), blacklist[i], strlen(blacklist[i])))
+ return true;
+ }
+ return false;
+}
+
+kallsyms_map_t read_kallsyms()
+{
+ kallsyms_map_t kallsyms;
+ FILE* f = fopen("/proc/kallsyms", "r");
+ if (f == NULL)
+ failf("failed to open /proc/kallsyms");
+ size_t n = 0;
+ char* line = NULL;
+ for (;;) {
+ ssize_t len = getline(&line, &n, f);
+ if (len < 0)
+ break;
+ long long pc;
+ char typ;
+ char sym[1024];
+ if (sscanf(line, "%016llx %c %s\n", &pc, &typ, sym) != 3)
+ failf("bad line in kallsyms: %s", line);
+ if (typ != 't' && typ != 'T')
+ continue;
+ kallsyms[pc] = sym;
+ }
+ free(line);
+ fclose(f);
+ return kallsyms;
+}
+
+void failf(const char* msg, ...)
+{
+ int e = errno;
+ va_list args;
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, " (errno: %s)\n", strerror(e));
+ exit(1);
+}