diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2019-12-03 11:34:27 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2019-12-03 18:48:14 +0100 |
| commit | d20ee9bd98f1a0e98c2d02da825acf3628562e8d (patch) | |
| tree | 6f4cfeb81e6a6beff7ffc87e6b55ae489f77d1bf /tools/fops_probe | |
| parent | a715d760ae486407f27f62815428da011d06e8dd (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.cc | 170 |
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); +} |
