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/sched.h> 10 #include <linux/seq_file.h> 11 #include <linux/slab.h> 12 #include <linux/sort.h> 13 #include <linux/string.h> 14 #include <linux/uaccess.h> 15 16 #include "kcsan.h" 17 18 /* 19 * Statistics counters. 20 */ 21 static atomic_long_t counters[KCSAN_COUNTER_COUNT]; 22 23 /* 24 * Addresses for filtering functions from reporting. This list can be used as a 25 * whitelist or blacklist. 26 */ 27 static struct { 28 unsigned long *addrs; /* array of addresses */ 29 size_t size; /* current size */ 30 int used; /* number of elements used */ 31 bool sorted; /* if elements are sorted */ 32 bool whitelist; /* if list is a blacklist or whitelist */ 33 } report_filterlist = { 34 .addrs = NULL, 35 .size = 8, /* small initial size */ 36 .used = 0, 37 .sorted = false, 38 .whitelist = false, /* default is blacklist */ 39 }; 40 static DEFINE_SPINLOCK(report_filterlist_lock); 41 42 static const char *counter_to_name(enum kcsan_counter_id id) 43 { 44 switch (id) { 45 case KCSAN_COUNTER_USED_WATCHPOINTS: return "used_watchpoints"; 46 case KCSAN_COUNTER_SETUP_WATCHPOINTS: return "setup_watchpoints"; 47 case KCSAN_COUNTER_DATA_RACES: return "data_races"; 48 case KCSAN_COUNTER_ASSERT_FAILURES: return "assert_failures"; 49 case KCSAN_COUNTER_NO_CAPACITY: return "no_capacity"; 50 case KCSAN_COUNTER_REPORT_RACES: return "report_races"; 51 case KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN: return "races_unknown_origin"; 52 case KCSAN_COUNTER_UNENCODABLE_ACCESSES: return "unencodable_accesses"; 53 case KCSAN_COUNTER_ENCODING_FALSE_POSITIVES: return "encoding_false_positives"; 54 case KCSAN_COUNTER_COUNT: 55 BUG(); 56 } 57 return NULL; 58 } 59 60 void kcsan_counter_inc(enum kcsan_counter_id id) 61 { 62 atomic_long_inc(&counters[id]); 63 } 64 65 void kcsan_counter_dec(enum kcsan_counter_id id) 66 { 67 atomic_long_dec(&counters[id]); 68 } 69 70 /* 71 * The microbenchmark allows benchmarking KCSAN core runtime only. To run 72 * multiple threads, pipe 'microbench=<iters>' from multiple tasks into the 73 * debugfs file. This will not generate any conflicts, and tests fast-path only. 74 */ 75 static noinline void microbenchmark(unsigned long iters) 76 { 77 const struct kcsan_ctx ctx_save = current->kcsan_ctx; 78 const bool was_enabled = READ_ONCE(kcsan_enabled); 79 cycles_t cycles; 80 81 /* We may have been called from an atomic region; reset context. */ 82 memset(¤t->kcsan_ctx, 0, sizeof(current->kcsan_ctx)); 83 /* 84 * Disable to benchmark fast-path for all accesses, and (expected 85 * negligible) call into slow-path, but never set up watchpoints. 86 */ 87 WRITE_ONCE(kcsan_enabled, false); 88 89 pr_info("KCSAN: %s begin | iters: %lu\n", __func__, iters); 90 91 cycles = get_cycles(); 92 while (iters--) { 93 unsigned long addr = iters & ((PAGE_SIZE << 8) - 1); 94 int type = !(iters & 0x7f) ? KCSAN_ACCESS_ATOMIC : 95 (!(iters & 0xf) ? KCSAN_ACCESS_WRITE : 0); 96 __kcsan_check_access((void *)addr, sizeof(long), type); 97 } 98 cycles = get_cycles() - cycles; 99 100 pr_info("KCSAN: %s end | cycles: %llu\n", __func__, cycles); 101 102 WRITE_ONCE(kcsan_enabled, was_enabled); 103 /* restore context */ 104 current->kcsan_ctx = ctx_save; 105 } 106 107 /* 108 * Simple test to create conflicting accesses. Write 'test=<iters>' to KCSAN's 109 * debugfs file from multiple tasks to generate real conflicts and show reports. 110 */ 111 static long test_dummy; 112 static long test_flags; 113 static noinline void test_thread(unsigned long iters) 114 { 115 const long CHANGE_BITS = 0xff00ff00ff00ff00L; 116 const struct kcsan_ctx ctx_save = current->kcsan_ctx; 117 cycles_t cycles; 118 119 /* We may have been called from an atomic region; reset context. */ 120 memset(¤t->kcsan_ctx, 0, sizeof(current->kcsan_ctx)); 121 122 pr_info("KCSAN: %s begin | iters: %lu\n", __func__, iters); 123 pr_info("test_dummy@%px, test_flags@%px\n", &test_dummy, &test_flags); 124 125 cycles = get_cycles(); 126 while (iters--) { 127 /* These all should generate reports. */ 128 __kcsan_check_read(&test_dummy, sizeof(test_dummy)); 129 ASSERT_EXCLUSIVE_WRITER(test_dummy); 130 ASSERT_EXCLUSIVE_ACCESS(test_dummy); 131 132 ASSERT_EXCLUSIVE_BITS(test_flags, ~CHANGE_BITS); /* no report */ 133 __kcsan_check_read(&test_flags, sizeof(test_flags)); /* no report */ 134 135 ASSERT_EXCLUSIVE_BITS(test_flags, CHANGE_BITS); /* report */ 136 __kcsan_check_read(&test_flags, sizeof(test_flags)); /* no report */ 137 138 /* not actually instrumented */ 139 WRITE_ONCE(test_dummy, iters); /* to observe value-change */ 140 __kcsan_check_write(&test_dummy, sizeof(test_dummy)); 141 142 test_flags ^= CHANGE_BITS; /* generate value-change */ 143 __kcsan_check_write(&test_flags, sizeof(test_flags)); 144 } 145 cycles = get_cycles() - cycles; 146 147 pr_info("KCSAN: %s end | cycles: %llu\n", __func__, cycles); 148 149 /* restore context */ 150 current->kcsan_ctx = ctx_save; 151 } 152 153 static int cmp_filterlist_addrs(const void *rhs, const void *lhs) 154 { 155 const unsigned long a = *(const unsigned long *)rhs; 156 const unsigned long b = *(const unsigned long *)lhs; 157 158 return a < b ? -1 : a == b ? 0 : 1; 159 } 160 161 bool kcsan_skip_report_debugfs(unsigned long func_addr) 162 { 163 unsigned long symbolsize, offset; 164 unsigned long flags; 165 bool ret = false; 166 167 if (!kallsyms_lookup_size_offset(func_addr, &symbolsize, &offset)) 168 return false; 169 func_addr -= offset; /* Get function start */ 170 171 spin_lock_irqsave(&report_filterlist_lock, flags); 172 if (report_filterlist.used == 0) 173 goto out; 174 175 /* Sort array if it is unsorted, and then do a binary search. */ 176 if (!report_filterlist.sorted) { 177 sort(report_filterlist.addrs, report_filterlist.used, 178 sizeof(unsigned long), cmp_filterlist_addrs, NULL); 179 report_filterlist.sorted = true; 180 } 181 ret = !!bsearch(&func_addr, report_filterlist.addrs, 182 report_filterlist.used, sizeof(unsigned long), 183 cmp_filterlist_addrs); 184 if (report_filterlist.whitelist) 185 ret = !ret; 186 187 out: 188 spin_unlock_irqrestore(&report_filterlist_lock, flags); 189 return ret; 190 } 191 192 static void set_report_filterlist_whitelist(bool whitelist) 193 { 194 unsigned long flags; 195 196 spin_lock_irqsave(&report_filterlist_lock, flags); 197 report_filterlist.whitelist = whitelist; 198 spin_unlock_irqrestore(&report_filterlist_lock, flags); 199 } 200 201 /* Returns 0 on success, error-code otherwise. */ 202 static ssize_t insert_report_filterlist(const char *func) 203 { 204 unsigned long flags; 205 unsigned long addr = kallsyms_lookup_name(func); 206 ssize_t ret = 0; 207 208 if (!addr) { 209 pr_err("KCSAN: could not find function: '%s'\n", func); 210 return -ENOENT; 211 } 212 213 spin_lock_irqsave(&report_filterlist_lock, flags); 214 215 if (report_filterlist.addrs == NULL) { 216 /* initial allocation */ 217 report_filterlist.addrs = 218 kmalloc_array(report_filterlist.size, 219 sizeof(unsigned long), GFP_KERNEL); 220 if (report_filterlist.addrs == NULL) { 221 ret = -ENOMEM; 222 goto out; 223 } 224 } else if (report_filterlist.used == report_filterlist.size) { 225 /* resize filterlist */ 226 size_t new_size = report_filterlist.size * 2; 227 unsigned long *new_addrs = 228 krealloc(report_filterlist.addrs, 229 new_size * sizeof(unsigned long), GFP_KERNEL); 230 231 if (new_addrs == NULL) { 232 /* leave filterlist itself untouched */ 233 ret = -ENOMEM; 234 goto out; 235 } 236 237 report_filterlist.size = new_size; 238 report_filterlist.addrs = new_addrs; 239 } 240 241 /* Note: deduplicating should be done in userspace. */ 242 report_filterlist.addrs[report_filterlist.used++] = 243 kallsyms_lookup_name(func); 244 report_filterlist.sorted = false; 245 246 out: 247 spin_unlock_irqrestore(&report_filterlist_lock, flags); 248 249 return ret; 250 } 251 252 static int show_info(struct seq_file *file, void *v) 253 { 254 int i; 255 unsigned long flags; 256 257 /* show stats */ 258 seq_printf(file, "enabled: %i\n", READ_ONCE(kcsan_enabled)); 259 for (i = 0; i < KCSAN_COUNTER_COUNT; ++i) 260 seq_printf(file, "%s: %ld\n", counter_to_name(i), 261 atomic_long_read(&counters[i])); 262 263 /* show filter functions, and filter type */ 264 spin_lock_irqsave(&report_filterlist_lock, flags); 265 seq_printf(file, "\n%s functions: %s\n", 266 report_filterlist.whitelist ? "whitelisted" : "blacklisted", 267 report_filterlist.used == 0 ? "none" : ""); 268 for (i = 0; i < report_filterlist.used; ++i) 269 seq_printf(file, " %ps\n", (void *)report_filterlist.addrs[i]); 270 spin_unlock_irqrestore(&report_filterlist_lock, flags); 271 272 return 0; 273 } 274 275 static int debugfs_open(struct inode *inode, struct file *file) 276 { 277 return single_open(file, show_info, NULL); 278 } 279 280 static ssize_t 281 debugfs_write(struct file *file, const char __user *buf, size_t count, loff_t *off) 282 { 283 char kbuf[KSYM_NAME_LEN]; 284 char *arg; 285 int read_len = count < (sizeof(kbuf) - 1) ? count : (sizeof(kbuf) - 1); 286 287 if (copy_from_user(kbuf, buf, read_len)) 288 return -EFAULT; 289 kbuf[read_len] = '\0'; 290 arg = strstrip(kbuf); 291 292 if (!strcmp(arg, "on")) { 293 WRITE_ONCE(kcsan_enabled, true); 294 } else if (!strcmp(arg, "off")) { 295 WRITE_ONCE(kcsan_enabled, false); 296 } else if (!strncmp(arg, "microbench=", sizeof("microbench=") - 1)) { 297 unsigned long iters; 298 299 if (kstrtoul(&arg[sizeof("microbench=") - 1], 0, &iters)) 300 return -EINVAL; 301 microbenchmark(iters); 302 } else if (!strncmp(arg, "test=", sizeof("test=") - 1)) { 303 unsigned long iters; 304 305 if (kstrtoul(&arg[sizeof("test=") - 1], 0, &iters)) 306 return -EINVAL; 307 test_thread(iters); 308 } else if (!strcmp(arg, "whitelist")) { 309 set_report_filterlist_whitelist(true); 310 } else if (!strcmp(arg, "blacklist")) { 311 set_report_filterlist_whitelist(false); 312 } else if (arg[0] == '!') { 313 ssize_t ret = insert_report_filterlist(&arg[1]); 314 315 if (ret < 0) 316 return ret; 317 } else { 318 return -EINVAL; 319 } 320 321 return count; 322 } 323 324 static const struct file_operations debugfs_ops = 325 { 326 .read = seq_read, 327 .open = debugfs_open, 328 .write = debugfs_write, 329 .release = single_release 330 }; 331 332 void __init kcsan_debugfs_init(void) 333 { 334 debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops); 335 } 336