1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/atomic.h> 4 #include <linux/bsearch.h> 5 #include <linux/bug.h> 6 #include <linux/debugfs.h> 7 #include <linux/init.h> 8 #include <linux/kallsyms.h> 9 #include <linux/seq_file.h> 10 #include <linux/slab.h> 11 #include <linux/sort.h> 12 #include <linux/string.h> 13 #include <linux/uaccess.h> 14 15 #include "kcsan.h" 16 17 /* 18 * Statistics counters. 19 */ 20 static atomic_long_t counters[KCSAN_COUNTER_COUNT]; 21 22 /* 23 * Addresses for filtering functions from reporting. This list can be used as a 24 * whitelist or blacklist. 25 */ 26 static struct { 27 unsigned long *addrs; /* array of addresses */ 28 size_t size; /* current size */ 29 int used; /* number of elements used */ 30 bool sorted; /* if elements are sorted */ 31 bool whitelist; /* if list is a blacklist or whitelist */ 32 } report_filterlist = { 33 .addrs = NULL, 34 .size = 8, /* small initial size */ 35 .used = 0, 36 .sorted = false, 37 .whitelist = false, /* default is blacklist */ 38 }; 39 static DEFINE_SPINLOCK(report_filterlist_lock); 40 41 static const char *counter_to_name(enum kcsan_counter_id id) 42 { 43 switch (id) { 44 case KCSAN_COUNTER_USED_WATCHPOINTS: 45 return "used_watchpoints"; 46 case KCSAN_COUNTER_SETUP_WATCHPOINTS: 47 return "setup_watchpoints"; 48 case KCSAN_COUNTER_DATA_RACES: 49 return "data_races"; 50 case KCSAN_COUNTER_NO_CAPACITY: 51 return "no_capacity"; 52 case KCSAN_COUNTER_REPORT_RACES: 53 return "report_races"; 54 case KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN: 55 return "races_unknown_origin"; 56 case KCSAN_COUNTER_UNENCODABLE_ACCESSES: 57 return "unencodable_accesses"; 58 case KCSAN_COUNTER_ENCODING_FALSE_POSITIVES: 59 return "encoding_false_positives"; 60 case KCSAN_COUNTER_COUNT: 61 BUG(); 62 } 63 return NULL; 64 } 65 66 void kcsan_counter_inc(enum kcsan_counter_id id) 67 { 68 atomic_long_inc(&counters[id]); 69 } 70 71 void kcsan_counter_dec(enum kcsan_counter_id id) 72 { 73 atomic_long_dec(&counters[id]); 74 } 75 76 /* 77 * The microbenchmark allows benchmarking KCSAN core runtime only. To run 78 * multiple threads, pipe 'microbench=<iters>' from multiple tasks into the 79 * debugfs file. 80 */ 81 static void microbenchmark(unsigned long iters) 82 { 83 cycles_t cycles; 84 85 pr_info("KCSAN: %s begin | iters: %lu\n", __func__, iters); 86 87 cycles = get_cycles(); 88 while (iters--) { 89 /* 90 * We can run this benchmark from multiple tasks; this address 91 * calculation increases likelyhood of some accesses overlapping 92 * (they still won't conflict because all are reads). 93 */ 94 unsigned long addr = 95 iters % (CONFIG_KCSAN_NUM_WATCHPOINTS * PAGE_SIZE); 96 __kcsan_check_read((void *)addr, sizeof(long)); 97 } 98 cycles = get_cycles() - cycles; 99 100 pr_info("KCSAN: %s end | cycles: %llu\n", __func__, cycles); 101 } 102 103 static int cmp_filterlist_addrs(const void *rhs, const void *lhs) 104 { 105 const unsigned long a = *(const unsigned long *)rhs; 106 const unsigned long b = *(const unsigned long *)lhs; 107 108 return a < b ? -1 : a == b ? 0 : 1; 109 } 110 111 bool kcsan_skip_report_debugfs(unsigned long func_addr) 112 { 113 unsigned long symbolsize, offset; 114 unsigned long flags; 115 bool ret = false; 116 117 if (!kallsyms_lookup_size_offset(func_addr, &symbolsize, &offset)) 118 return false; 119 func_addr -= offset; /* get function start */ 120 121 spin_lock_irqsave(&report_filterlist_lock, flags); 122 if (report_filterlist.used == 0) 123 goto out; 124 125 /* Sort array if it is unsorted, and then do a binary search. */ 126 if (!report_filterlist.sorted) { 127 sort(report_filterlist.addrs, report_filterlist.used, 128 sizeof(unsigned long), cmp_filterlist_addrs, NULL); 129 report_filterlist.sorted = true; 130 } 131 ret = !!bsearch(&func_addr, report_filterlist.addrs, 132 report_filterlist.used, sizeof(unsigned long), 133 cmp_filterlist_addrs); 134 if (report_filterlist.whitelist) 135 ret = !ret; 136 137 out: 138 spin_unlock_irqrestore(&report_filterlist_lock, flags); 139 return ret; 140 } 141 142 static void set_report_filterlist_whitelist(bool whitelist) 143 { 144 unsigned long flags; 145 146 spin_lock_irqsave(&report_filterlist_lock, flags); 147 report_filterlist.whitelist = whitelist; 148 spin_unlock_irqrestore(&report_filterlist_lock, flags); 149 } 150 151 /* Returns 0 on success, error-code otherwise. */ 152 static ssize_t insert_report_filterlist(const char *func) 153 { 154 unsigned long flags; 155 unsigned long addr = kallsyms_lookup_name(func); 156 ssize_t ret = 0; 157 158 if (!addr) { 159 pr_err("KCSAN: could not find function: '%s'\n", func); 160 return -ENOENT; 161 } 162 163 spin_lock_irqsave(&report_filterlist_lock, flags); 164 165 if (report_filterlist.addrs == NULL) { 166 /* initial allocation */ 167 report_filterlist.addrs = 168 kmalloc_array(report_filterlist.size, 169 sizeof(unsigned long), GFP_KERNEL); 170 if (report_filterlist.addrs == NULL) { 171 ret = -ENOMEM; 172 goto out; 173 } 174 } else if (report_filterlist.used == report_filterlist.size) { 175 /* resize filterlist */ 176 size_t new_size = report_filterlist.size * 2; 177 unsigned long *new_addrs = 178 krealloc(report_filterlist.addrs, 179 new_size * sizeof(unsigned long), GFP_KERNEL); 180 181 if (new_addrs == NULL) { 182 /* leave filterlist itself untouched */ 183 ret = -ENOMEM; 184 goto out; 185 } 186 187 report_filterlist.size = new_size; 188 report_filterlist.addrs = new_addrs; 189 } 190 191 /* Note: deduplicating should be done in userspace. */ 192 report_filterlist.addrs[report_filterlist.used++] = 193 kallsyms_lookup_name(func); 194 report_filterlist.sorted = false; 195 196 out: 197 spin_unlock_irqrestore(&report_filterlist_lock, flags); 198 return ret; 199 } 200 201 static int show_info(struct seq_file *file, void *v) 202 { 203 int i; 204 unsigned long flags; 205 206 /* show stats */ 207 seq_printf(file, "enabled: %i\n", READ_ONCE(kcsan_enabled)); 208 for (i = 0; i < KCSAN_COUNTER_COUNT; ++i) 209 seq_printf(file, "%s: %ld\n", counter_to_name(i), 210 atomic_long_read(&counters[i])); 211 212 /* show filter functions, and filter type */ 213 spin_lock_irqsave(&report_filterlist_lock, flags); 214 seq_printf(file, "\n%s functions: %s\n", 215 report_filterlist.whitelist ? "whitelisted" : "blacklisted", 216 report_filterlist.used == 0 ? "none" : ""); 217 for (i = 0; i < report_filterlist.used; ++i) 218 seq_printf(file, " %ps\n", (void *)report_filterlist.addrs[i]); 219 spin_unlock_irqrestore(&report_filterlist_lock, flags); 220 221 return 0; 222 } 223 224 static int debugfs_open(struct inode *inode, struct file *file) 225 { 226 return single_open(file, show_info, NULL); 227 } 228 229 static ssize_t debugfs_write(struct file *file, const char __user *buf, 230 size_t count, loff_t *off) 231 { 232 char kbuf[KSYM_NAME_LEN]; 233 char *arg; 234 int read_len = count < (sizeof(kbuf) - 1) ? count : (sizeof(kbuf) - 1); 235 236 if (copy_from_user(kbuf, buf, read_len)) 237 return -EFAULT; 238 kbuf[read_len] = '\0'; 239 arg = strstrip(kbuf); 240 241 if (!strcmp(arg, "on")) { 242 WRITE_ONCE(kcsan_enabled, true); 243 } else if (!strcmp(arg, "off")) { 244 WRITE_ONCE(kcsan_enabled, false); 245 } else if (!strncmp(arg, "microbench=", sizeof("microbench=") - 1)) { 246 unsigned long iters; 247 248 if (kstrtoul(&arg[sizeof("microbench=") - 1], 0, &iters)) 249 return -EINVAL; 250 microbenchmark(iters); 251 } else if (!strcmp(arg, "whitelist")) { 252 set_report_filterlist_whitelist(true); 253 } else if (!strcmp(arg, "blacklist")) { 254 set_report_filterlist_whitelist(false); 255 } else if (arg[0] == '!') { 256 ssize_t ret = insert_report_filterlist(&arg[1]); 257 258 if (ret < 0) 259 return ret; 260 } else { 261 return -EINVAL; 262 } 263 264 return count; 265 } 266 267 static const struct file_operations debugfs_ops = { .read = seq_read, 268 .open = debugfs_open, 269 .write = debugfs_write, 270 .release = single_release }; 271 272 void __init kcsan_debugfs_init(void) 273 { 274 debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops); 275 } 276