1 // SPDX-License-Identifier: GPL-2.0
3 #define pr_fmt(fmt) "kcsan: " fmt
5 #include <linux/atomic.h>
6 #include <linux/bsearch.h>
8 #include <linux/debugfs.h>
9 #include <linux/init.h>
10 #include <linux/kallsyms.h>
11 #include <linux/sched.h>
12 #include <linux/seq_file.h>
13 #include <linux/slab.h>
14 #include <linux/sort.h>
15 #include <linux/string.h>
16 #include <linux/uaccess.h>
20 atomic_long_t kcsan_counters
[KCSAN_COUNTER_COUNT
];
21 static const char *const counter_names
[] = {
22 [KCSAN_COUNTER_USED_WATCHPOINTS
] = "used_watchpoints",
23 [KCSAN_COUNTER_SETUP_WATCHPOINTS
] = "setup_watchpoints",
24 [KCSAN_COUNTER_DATA_RACES
] = "data_races",
25 [KCSAN_COUNTER_ASSERT_FAILURES
] = "assert_failures",
26 [KCSAN_COUNTER_NO_CAPACITY
] = "no_capacity",
27 [KCSAN_COUNTER_REPORT_RACES
] = "report_races",
28 [KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN
] = "races_unknown_origin",
29 [KCSAN_COUNTER_UNENCODABLE_ACCESSES
] = "unencodable_accesses",
30 [KCSAN_COUNTER_ENCODING_FALSE_POSITIVES
] = "encoding_false_positives",
32 static_assert(ARRAY_SIZE(counter_names
) == KCSAN_COUNTER_COUNT
);
35 * Addresses for filtering functions from reporting. This list can be used as a
36 * whitelist or blacklist.
39 unsigned long *addrs
; /* array of addresses */
40 size_t size
; /* current size */
41 int used
; /* number of elements used */
42 bool sorted
; /* if elements are sorted */
43 bool whitelist
; /* if list is a blacklist or whitelist */
44 } report_filterlist
= {
46 .size
= 8, /* small initial size */
49 .whitelist
= false, /* default is blacklist */
51 static DEFINE_SPINLOCK(report_filterlist_lock
);
54 * The microbenchmark allows benchmarking KCSAN core runtime only. To run
55 * multiple threads, pipe 'microbench=<iters>' from multiple tasks into the
56 * debugfs file. This will not generate any conflicts, and tests fast-path only.
58 static noinline
void microbenchmark(unsigned long iters
)
60 const struct kcsan_ctx ctx_save
= current
->kcsan_ctx
;
61 const bool was_enabled
= READ_ONCE(kcsan_enabled
);
64 /* We may have been called from an atomic region; reset context. */
65 memset(¤t
->kcsan_ctx
, 0, sizeof(current
->kcsan_ctx
));
67 * Disable to benchmark fast-path for all accesses, and (expected
68 * negligible) call into slow-path, but never set up watchpoints.
70 WRITE_ONCE(kcsan_enabled
, false);
72 pr_info("%s begin | iters: %lu\n", __func__
, iters
);
74 cycles
= get_cycles();
76 unsigned long addr
= iters
& ((PAGE_SIZE
<< 8) - 1);
77 int type
= !(iters
& 0x7f) ? KCSAN_ACCESS_ATOMIC
:
78 (!(iters
& 0xf) ? KCSAN_ACCESS_WRITE
: 0);
79 __kcsan_check_access((void *)addr
, sizeof(long), type
);
81 cycles
= get_cycles() - cycles
;
83 pr_info("%s end | cycles: %llu\n", __func__
, cycles
);
85 WRITE_ONCE(kcsan_enabled
, was_enabled
);
87 current
->kcsan_ctx
= ctx_save
;
90 static int cmp_filterlist_addrs(const void *rhs
, const void *lhs
)
92 const unsigned long a
= *(const unsigned long *)rhs
;
93 const unsigned long b
= *(const unsigned long *)lhs
;
95 return a
< b
? -1 : a
== b
? 0 : 1;
98 bool kcsan_skip_report_debugfs(unsigned long func_addr
)
100 unsigned long symbolsize
, offset
;
104 if (!kallsyms_lookup_size_offset(func_addr
, &symbolsize
, &offset
))
106 func_addr
-= offset
; /* Get function start */
108 spin_lock_irqsave(&report_filterlist_lock
, flags
);
109 if (report_filterlist
.used
== 0)
112 /* Sort array if it is unsorted, and then do a binary search. */
113 if (!report_filterlist
.sorted
) {
114 sort(report_filterlist
.addrs
, report_filterlist
.used
,
115 sizeof(unsigned long), cmp_filterlist_addrs
, NULL
);
116 report_filterlist
.sorted
= true;
118 ret
= !!bsearch(&func_addr
, report_filterlist
.addrs
,
119 report_filterlist
.used
, sizeof(unsigned long),
120 cmp_filterlist_addrs
);
121 if (report_filterlist
.whitelist
)
125 spin_unlock_irqrestore(&report_filterlist_lock
, flags
);
129 static void set_report_filterlist_whitelist(bool whitelist
)
133 spin_lock_irqsave(&report_filterlist_lock
, flags
);
134 report_filterlist
.whitelist
= whitelist
;
135 spin_unlock_irqrestore(&report_filterlist_lock
, flags
);
138 /* Returns 0 on success, error-code otherwise. */
139 static ssize_t
insert_report_filterlist(const char *func
)
142 unsigned long addr
= kallsyms_lookup_name(func
);
146 pr_err("could not find function: '%s'\n", func
);
150 spin_lock_irqsave(&report_filterlist_lock
, flags
);
152 if (report_filterlist
.addrs
== NULL
) {
153 /* initial allocation */
154 report_filterlist
.addrs
=
155 kmalloc_array(report_filterlist
.size
,
156 sizeof(unsigned long), GFP_ATOMIC
);
157 if (report_filterlist
.addrs
== NULL
) {
161 } else if (report_filterlist
.used
== report_filterlist
.size
) {
162 /* resize filterlist */
163 size_t new_size
= report_filterlist
.size
* 2;
164 unsigned long *new_addrs
=
165 krealloc(report_filterlist
.addrs
,
166 new_size
* sizeof(unsigned long), GFP_ATOMIC
);
168 if (new_addrs
== NULL
) {
169 /* leave filterlist itself untouched */
174 report_filterlist
.size
= new_size
;
175 report_filterlist
.addrs
= new_addrs
;
178 /* Note: deduplicating should be done in userspace. */
179 report_filterlist
.addrs
[report_filterlist
.used
++] =
180 kallsyms_lookup_name(func
);
181 report_filterlist
.sorted
= false;
184 spin_unlock_irqrestore(&report_filterlist_lock
, flags
);
189 static int show_info(struct seq_file
*file
, void *v
)
195 seq_printf(file
, "enabled: %i\n", READ_ONCE(kcsan_enabled
));
196 for (i
= 0; i
< KCSAN_COUNTER_COUNT
; ++i
) {
197 seq_printf(file
, "%s: %ld\n", counter_names
[i
],
198 atomic_long_read(&kcsan_counters
[i
]));
201 /* show filter functions, and filter type */
202 spin_lock_irqsave(&report_filterlist_lock
, flags
);
203 seq_printf(file
, "\n%s functions: %s\n",
204 report_filterlist
.whitelist
? "whitelisted" : "blacklisted",
205 report_filterlist
.used
== 0 ? "none" : "");
206 for (i
= 0; i
< report_filterlist
.used
; ++i
)
207 seq_printf(file
, " %ps\n", (void *)report_filterlist
.addrs
[i
]);
208 spin_unlock_irqrestore(&report_filterlist_lock
, flags
);
213 static int debugfs_open(struct inode
*inode
, struct file
*file
)
215 return single_open(file
, show_info
, NULL
);
219 debugfs_write(struct file
*file
, const char __user
*buf
, size_t count
, loff_t
*off
)
221 char kbuf
[KSYM_NAME_LEN
];
223 int read_len
= count
< (sizeof(kbuf
) - 1) ? count
: (sizeof(kbuf
) - 1);
225 if (copy_from_user(kbuf
, buf
, read_len
))
227 kbuf
[read_len
] = '\0';
228 arg
= strstrip(kbuf
);
230 if (!strcmp(arg
, "on")) {
231 WRITE_ONCE(kcsan_enabled
, true);
232 } else if (!strcmp(arg
, "off")) {
233 WRITE_ONCE(kcsan_enabled
, false);
234 } else if (str_has_prefix(arg
, "microbench=")) {
237 if (kstrtoul(&arg
[strlen("microbench=")], 0, &iters
))
239 microbenchmark(iters
);
240 } else if (!strcmp(arg
, "whitelist")) {
241 set_report_filterlist_whitelist(true);
242 } else if (!strcmp(arg
, "blacklist")) {
243 set_report_filterlist_whitelist(false);
244 } else if (arg
[0] == '!') {
245 ssize_t ret
= insert_report_filterlist(&arg
[1]);
256 static const struct file_operations debugfs_ops
=
259 .open
= debugfs_open
,
260 .write
= debugfs_write
,
261 .release
= single_release
264 void __init
kcsan_debugfs_init(void)
266 debugfs_create_file("kcsan", 0644, NULL
, NULL
, &debugfs_ops
);