github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/tools/fops_probe/fops_probe.cc (about)

     1  // Copyright 2019 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  // fops_probe utility helps to understand what file_operations callbacks
     5  // are attached to a particular file. Requries KCOV and KALLSYMS.
     6  // Build with:
     7  //	g++ tools/fops_probe/fops_probe.cc -Wall -static -o fops_probe
     8  // Then copy the binary to target machine and run as:
     9  //	./fops_probe /dev/fb0
    10  // You should see output similar to:
    11  //
    12  //	ffffffff81bcccb9 vfs_read
    13  //	................
    14  //	ffffffff83af85c3 fb_read
    15  //	ffffffff83b52af5 cirrusfb_sync
    16  //
    17  //	ffffffff81bcd219 vfs_write
    18  //	................
    19  //	ffffffff83af7fe2 fb_write
    20  //	ffffffff83b52af5 cirrusfb_sync
    21  //
    22  //	ffffffff81c1b745 do_vfs_ioctl
    23  //	ffffffff83af7ea9 fb_ioctl
    24  //
    25  //	ffffffff81a4ea44 do_mmap
    26  //	................
    27  //	ffffffff83af716c fb_mmap
    28  //
    29  // which allows to understand what callbacks are associated with /dev/fb0.
    30  
    31  #include <errno.h>
    32  #include <fcntl.h>
    33  #include <stdarg.h>
    34  #include <stddef.h>
    35  #include <stdint.h>
    36  #include <stdio.h>
    37  #include <stdlib.h>
    38  #include <string.h>
    39  #include <unistd.h>
    40  
    41  #include <linux/kcov.h>
    42  #include <sys/ioctl.h>
    43  #include <sys/mman.h>
    44  #include <sys/stat.h>
    45  #include <sys/types.h>
    46  
    47  #include <functional>
    48  #include <map>
    49  #include <set>
    50  #include <string>
    51  
    52  #define COVER_SIZE (1 << 20)
    53  
    54  typedef std::map<long long, std::string> kallsyms_map_t;
    55  
    56  static __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void failf(const char* msg, ...);
    57  static kallsyms_map_t read_kallsyms();
    58  static bool should_skip(const std::string& sym);
    59  static void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
    60  			   const std::string& start_sym, std::function<void(void)> fn);
    61  
    62  int main(int argc, char** argv)
    63  {
    64  	if (argc != 2)
    65  		failf("usage: fops_probe file");
    66  	int fd = open(argv[1], O_RDWR);
    67  	if (fd == -1) {
    68  		fd = open(argv[1], O_RDONLY);
    69  		if (fd == -1)
    70  			failf("failed to open %s", argv[1]);
    71  	}
    72  	const kallsyms_map_t kallsyms = read_kallsyms();
    73  	int kcov = open("/sys/kernel/debug/kcov", O_RDWR);
    74  	if (kcov == -1)
    75  		failf("failed to open /sys/kernel/debug/kcov");
    76  	if (ioctl(kcov, KCOV_INIT_TRACE, COVER_SIZE))
    77  		failf("KCOV_INIT_TRACE failed");
    78  	uint64_t* cover = (uint64_t*)mmap(NULL, COVER_SIZE * 8, PROT_READ | PROT_WRITE, MAP_SHARED, kcov, 0);
    79  	if (cover == MAP_FAILED)
    80  		failf("cover mmap failed");
    81  	if (ioctl(kcov, KCOV_ENABLE, KCOV_TRACE_PC))
    82  		failf("KCOV_ENABLE failed");
    83  	probe_callback(cover, kallsyms, "do_vfs_ioctl", [&]() { ioctl(fd, 0, 0); });
    84  	probe_callback(cover, kallsyms, "do_mmap", [&]() { mmap(0, 4096, PROT_READ, MAP_PRIVATE, fd, 0); });
    85  	probe_callback(cover, kallsyms, "vfs_write", [&]() { write(fd, 0, 0); });
    86  	probe_callback(cover, kallsyms, "vfs_read", [&]() { read(fd, 0, 0); });
    87  	return 0;
    88  }
    89  
    90  void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
    91  		    const std::string& start_sym, std::function<void(void)> fn)
    92  {
    93  	__atomic_store_n(&cover[0], 0, __ATOMIC_SEQ_CST);
    94  	fn();
    95  	uint64_t ncover = __atomic_load_n(&cover[0], __ATOMIC_SEQ_CST);
    96  	bool started = false;
    97  	std::set<std::string> seen;
    98  	for (uint64_t i = 0; i < ncover; i++) {
    99  		long long pc = cover[i + 1];
   100  		auto it = kallsyms.lower_bound(pc - 1);
   101  		const std::string& sym = it == kallsyms.begin() ? "" : (--it)->second;
   102  		if (!started && sym != start_sym)
   103  			continue;
   104  		started = true;
   105  		if (!seen.insert(sym).second || should_skip(sym))
   106  			continue;
   107  		printf("%0llx %s\n", pc, sym.c_str());
   108  	}
   109  	printf("\n");
   110  }
   111  
   112  bool should_skip(const std::string& sym)
   113  {
   114  	static const char* skip[] = {
   115  	    "security",
   116  	    "tomoyo",
   117  	    "selinux",
   118  	    "apparmor",
   119  	    "smack",
   120  	    "policy",
   121  	    "stack_trace",
   122  	    "should_fail",
   123  	    "debug",
   124  	    "trace",
   125  	    "snprintf",
   126  	    "vsnprintf",
   127  	};
   128  	for (size_t i = 0; i < sizeof(skip) / sizeof(skip[0]); i++) {
   129  		if (!strncmp(sym.c_str(), skip[i], strlen(skip[i])))
   130  			return true;
   131  	}
   132  	return false;
   133  }
   134  
   135  kallsyms_map_t read_kallsyms()
   136  {
   137  	kallsyms_map_t kallsyms;
   138  	FILE* f = fopen("/proc/kallsyms", "r");
   139  	if (f == NULL)
   140  		failf("failed to open /proc/kallsyms");
   141  	size_t n = 0;
   142  	char* line = NULL;
   143  	for (;;) {
   144  		ssize_t len = getline(&line, &n, f);
   145  		if (len < 0)
   146  			break;
   147  		long long pc;
   148  		char typ;
   149  		char sym[1024];
   150  		if (sscanf(line, "%016llx %c %s\n", &pc, &typ, sym) != 3)
   151  			failf("bad line in kallsyms: %s", line);
   152  		if (typ != 't' && typ != 'T')
   153  			continue;
   154  		kallsyms[pc] = sym;
   155  	}
   156  	free(line);
   157  	fclose(f);
   158  	return kallsyms;
   159  }
   160  
   161  void failf(const char* msg, ...)
   162  {
   163  	int e = errno;
   164  	va_list args;
   165  	va_start(args, msg);
   166  	vfprintf(stderr, msg, args);
   167  	va_end(args);
   168  	fprintf(stderr, " (errno: %s)\n", strerror(e));
   169  	exit(1);
   170  }