From d20ee9bd98f1a0e98c2d02da825acf3628562e8d Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 3 Dec 2019 11:34:27 +0100 Subject: 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. --- tools/fops_probe/fops_probe.cc | 170 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 tools/fops_probe/fops_probe.cc (limited to 'tools/fops_probe/fops_probe.cc') 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define COVER_SIZE (1 << 20) + +typedef std::map 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 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 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 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); +} -- cgit mrf-deployment