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