Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8bb31b9d | 2 | /* |
426f3a53 KC |
3 | * Linux Kernel Dump Test Module for testing kernel crashes conditions: |
4 | * induces system failures at predefined crashpoints and under predefined | |
5 | * operational conditions in order to evaluate the reliability of kernel | |
6 | * sanity checking and crash dumps obtained using different dumping | |
7 | * solutions. | |
8bb31b9d | 8 | * |
8bb31b9d AG |
9 | * Copyright (C) IBM Corporation, 2006 |
10 | * | |
11 | * Author: Ankita Garg <ankita@in.ibm.com> | |
12 | * | |
8bb31b9d AG |
13 | * It is adapted from the Linux Kernel Dump Test Tool by |
14 | * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net> | |
15 | * | |
0347af4e | 16 | * Debugfs support added by Simon Kagstrom <simon.kagstrom@netinsight.net> |
8bb31b9d | 17 | * |
10ffebbe | 18 | * See Documentation/fault-injection/provoke-crashes.rst for instructions |
8bb31b9d | 19 | */ |
6d2e91a6 | 20 | #include "lkdtm.h" |
5d861d92 | 21 | #include <linux/fs.h> |
8bb31b9d | 22 | #include <linux/module.h> |
5d861d92 | 23 | #include <linux/buffer_head.h> |
8bb31b9d | 24 | #include <linux/kprobes.h> |
5d861d92 | 25 | #include <linux/list.h> |
8bb31b9d | 26 | #include <linux/init.h> |
5a0e3ad6 | 27 | #include <linux/slab.h> |
0347af4e | 28 | #include <linux/debugfs.h> |
3a3a11e6 | 29 | #include <linux/utsname.h> |
8bb31b9d | 30 | |
d87c9788 KC |
31 | #define DEFAULT_COUNT 10 |
32 | ||
c479e3fd KC |
33 | static int lkdtm_debugfs_open(struct inode *inode, struct file *file); |
34 | static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, | |
35 | size_t count, loff_t *off); | |
36 | static ssize_t direct_entry(struct file *f, const char __user *user_buf, | |
37 | size_t count, loff_t *off); | |
2b271cb7 AB |
38 | |
39 | #ifdef CONFIG_KPROBES | |
31c5c870 | 40 | static int lkdtm_kprobe_handler(struct kprobe *kp, struct pt_regs *regs); |
c479e3fd KC |
41 | static ssize_t lkdtm_debugfs_entry(struct file *f, |
42 | const char __user *user_buf, | |
43 | size_t count, loff_t *off); | |
31c5c870 KC |
44 | # define CRASHPOINT_KPROBE(_symbol) \ |
45 | .kprobe = { \ | |
46 | .symbol_name = (_symbol), \ | |
47 | .pre_handler = lkdtm_kprobe_handler, \ | |
48 | }, | |
49 | # define CRASHPOINT_WRITE(_symbol) \ | |
50 | (_symbol) ? lkdtm_debugfs_entry : direct_entry | |
51 | #else | |
52 | # define CRASHPOINT_KPROBE(_symbol) | |
53 | # define CRASHPOINT_WRITE(_symbol) direct_entry | |
f2c6edc1 KC |
54 | #endif |
55 | ||
c479e3fd KC |
56 | /* Crash points */ |
57 | struct crashpoint { | |
58 | const char *name; | |
59 | const struct file_operations fops; | |
31c5c870 | 60 | struct kprobe kprobe; |
8bb31b9d AG |
61 | }; |
62 | ||
31c5c870 | 63 | #define CRASHPOINT(_name, _symbol) \ |
c479e3fd KC |
64 | { \ |
65 | .name = _name, \ | |
66 | .fops = { \ | |
67 | .read = lkdtm_debugfs_read, \ | |
68 | .llseek = generic_file_llseek, \ | |
69 | .open = lkdtm_debugfs_open, \ | |
31c5c870 | 70 | .write = CRASHPOINT_WRITE(_symbol) \ |
c479e3fd | 71 | }, \ |
31c5c870 | 72 | CRASHPOINT_KPROBE(_symbol) \ |
c479e3fd KC |
73 | } |
74 | ||
75 | /* Define the possible places where we can trigger a crash point. */ | |
31c5c870 KC |
76 | static struct crashpoint crashpoints[] = { |
77 | CRASHPOINT("DIRECT", NULL), | |
c479e3fd | 78 | #ifdef CONFIG_KPROBES |
31c5c870 | 79 | CRASHPOINT("INT_HARDWARE_ENTRY", "do_IRQ"), |
5be2a501 | 80 | CRASHPOINT("INT_HW_IRQ_EN", "handle_irq_event"), |
31c5c870 | 81 | CRASHPOINT("INT_TASKLET_ENTRY", "tasklet_action"), |
b290df06 | 82 | CRASHPOINT("FS_SUBMIT_BH", "submit_bh"), |
31c5c870 KC |
83 | CRASHPOINT("MEM_SWAPOUT", "shrink_inactive_list"), |
84 | CRASHPOINT("TIMERADD", "hrtimer_start"), | |
d1f278da | 85 | CRASHPOINT("SCSI_QUEUE_RQ", "scsi_queue_rq"), |
c479e3fd | 86 | #endif |
8bb31b9d AG |
87 | }; |
88 | ||
73f62e60 KC |
89 | /* List of possible types for crashes that can be triggered. */ |
90 | static const struct crashtype_category *crashtype_categories[] = { | |
91 | &bugs_crashtypes, | |
92 | &heap_crashtypes, | |
93 | &perms_crashtypes, | |
94 | &refcount_crashtypes, | |
95 | &usercopy_crashtypes, | |
96 | &stackleak_crashtypes, | |
97 | &cfi_crashtypes, | |
98 | &fortify_crashtypes, | |
387e220a | 99 | #ifdef CONFIG_PPC_64S_HASH_MMU |
73f62e60 | 100 | &powerpc_crashtypes, |
3ba150fb | 101 | #endif |
8bb31b9d AG |
102 | }; |
103 | ||
31c5c870 KC |
104 | /* Global kprobe entry and crashtype. */ |
105 | static struct kprobe *lkdtm_kprobe; | |
75f98b7a KC |
106 | static struct crashpoint *lkdtm_crashpoint; |
107 | static const struct crashtype *lkdtm_crashtype; | |
8bb31b9d | 108 | |
d87c9788 KC |
109 | /* Module parameters */ |
110 | static int recur_count = -1; | |
8bb31b9d | 111 | module_param(recur_count, int, 0644); |
7d196ac3 | 112 | MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test"); |
d87c9788 KC |
113 | |
114 | static char* cpoint_name; | |
dca41306 | 115 | module_param(cpoint_name, charp, 0444); |
5d861d92 | 116 | MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed"); |
d87c9788 KC |
117 | |
118 | static char* cpoint_type; | |
dca41306 | 119 | module_param(cpoint_type, charp, 0444); |
5d861d92 RD |
120 | MODULE_PARM_DESC(cpoint_type, " Crash Point Type, action to be taken on "\ |
121 | "hitting the crash point"); | |
d87c9788 KC |
122 | |
123 | static int cpoint_count = DEFAULT_COUNT; | |
5d861d92 RD |
124 | module_param(cpoint_count, int, 0644); |
125 | MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ | |
126 | "crash point is to be hit to trigger action"); | |
8bb31b9d | 127 | |
861dc0d7 KC |
128 | /* |
129 | * For test debug reporting when CI systems provide terse summaries. | |
130 | * TODO: Remove this once reasonable reporting exists in most CI systems: | |
131 | * https://lore.kernel.org/lkml/CAHk-=wiFvfkoFixTapvvyPMN9pq5G-+Dys2eSyBa1vzDGAO5+A@mail.gmail.com | |
132 | */ | |
3a3a11e6 | 133 | char *lkdtm_kernel_info; |
0347af4e | 134 | |
c479e3fd | 135 | /* Return the crashtype number or NULL if the name is invalid */ |
75f98b7a | 136 | static const struct crashtype *find_crashtype(const char *name) |
8bb31b9d | 137 | { |
73f62e60 KC |
138 | int cat, idx; |
139 | ||
140 | for (cat = 0; cat < ARRAY_SIZE(crashtype_categories); cat++) { | |
141 | for (idx = 0; idx < crashtype_categories[cat]->len; idx++) { | |
142 | struct crashtype *crashtype; | |
0347af4e | 143 | |
73f62e60 KC |
144 | crashtype = &crashtype_categories[cat]->crashtypes[idx]; |
145 | if (!strcmp(name, crashtype->name)) | |
146 | return crashtype; | |
147 | } | |
8bb31b9d AG |
148 | } |
149 | ||
c479e3fd | 150 | return NULL; |
8bb31b9d AG |
151 | } |
152 | ||
c479e3fd KC |
153 | /* |
154 | * This is forced noinline just so it distinctly shows up in the stackdump | |
155 | * which makes validation of expected lkdtm crashes easier. | |
84022cff DA |
156 | * |
157 | * NOTE: having a valid return value helps prevent the compiler from doing | |
158 | * tail call optimizations and taking this out of the stack trace. | |
c479e3fd | 159 | */ |
84022cff | 160 | static noinline int lkdtm_do_action(const struct crashtype *crashtype) |
8bb31b9d | 161 | { |
31c5c870 | 162 | if (WARN_ON(!crashtype || !crashtype->func)) |
84022cff | 163 | return -EINVAL; |
c479e3fd | 164 | crashtype->func(); |
84022cff DA |
165 | |
166 | return 0; | |
0347af4e SK |
167 | } |
168 | ||
c479e3fd | 169 | static int lkdtm_register_cpoint(struct crashpoint *crashpoint, |
75f98b7a | 170 | const struct crashtype *crashtype) |
8bb31b9d AG |
171 | { |
172 | int ret; | |
173 | ||
c479e3fd | 174 | /* If this doesn't have a symbol, just call immediately. */ |
84022cff DA |
175 | if (!crashpoint->kprobe.symbol_name) |
176 | return lkdtm_do_action(crashtype); | |
8bb31b9d | 177 | |
31c5c870 KC |
178 | if (lkdtm_kprobe != NULL) |
179 | unregister_kprobe(lkdtm_kprobe); | |
c479e3fd KC |
180 | |
181 | lkdtm_crashpoint = crashpoint; | |
182 | lkdtm_crashtype = crashtype; | |
31c5c870 KC |
183 | lkdtm_kprobe = &crashpoint->kprobe; |
184 | ret = register_kprobe(lkdtm_kprobe); | |
c479e3fd | 185 | if (ret < 0) { |
31c5c870 KC |
186 | pr_info("Couldn't register kprobe %s\n", |
187 | crashpoint->kprobe.symbol_name); | |
188 | lkdtm_kprobe = NULL; | |
c479e3fd KC |
189 | lkdtm_crashpoint = NULL; |
190 | lkdtm_crashtype = NULL; | |
0347af4e SK |
191 | } |
192 | ||
193 | return ret; | |
194 | } | |
195 | ||
2b271cb7 AB |
196 | #ifdef CONFIG_KPROBES |
197 | /* Global crash counter and spinlock. */ | |
198 | static int crash_count = DEFAULT_COUNT; | |
199 | static DEFINE_SPINLOCK(crash_count_lock); | |
200 | ||
31c5c870 KC |
201 | /* Called by kprobe entry points. */ |
202 | static int lkdtm_kprobe_handler(struct kprobe *kp, struct pt_regs *regs) | |
2b271cb7 AB |
203 | { |
204 | unsigned long flags; | |
205 | bool do_it = false; | |
206 | ||
31c5c870 KC |
207 | if (WARN_ON(!lkdtm_crashpoint || !lkdtm_crashtype)) |
208 | return 0; | |
2b271cb7 AB |
209 | |
210 | spin_lock_irqsave(&crash_count_lock, flags); | |
211 | crash_count--; | |
212 | pr_info("Crash point %s of type %s hit, trigger in %d rounds\n", | |
213 | lkdtm_crashpoint->name, lkdtm_crashtype->name, crash_count); | |
214 | ||
215 | if (crash_count == 0) { | |
216 | do_it = true; | |
217 | crash_count = cpoint_count; | |
218 | } | |
219 | spin_unlock_irqrestore(&crash_count_lock, flags); | |
220 | ||
221 | if (do_it) | |
84022cff | 222 | return lkdtm_do_action(lkdtm_crashtype); |
31c5c870 KC |
223 | |
224 | return 0; | |
2b271cb7 AB |
225 | } |
226 | ||
c479e3fd KC |
227 | static ssize_t lkdtm_debugfs_entry(struct file *f, |
228 | const char __user *user_buf, | |
229 | size_t count, loff_t *off) | |
0347af4e | 230 | { |
c479e3fd | 231 | struct crashpoint *crashpoint = file_inode(f)->i_private; |
75f98b7a | 232 | const struct crashtype *crashtype = NULL; |
0347af4e SK |
233 | char *buf; |
234 | int err; | |
235 | ||
236 | if (count >= PAGE_SIZE) | |
237 | return -EINVAL; | |
238 | ||
239 | buf = (char *)__get_free_page(GFP_KERNEL); | |
240 | if (!buf) | |
241 | return -ENOMEM; | |
242 | if (copy_from_user(buf, user_buf, count)) { | |
243 | free_page((unsigned long) buf); | |
244 | return -EFAULT; | |
245 | } | |
246 | /* NULL-terminate and remove enter */ | |
247 | buf[count] = '\0'; | |
248 | strim(buf); | |
249 | ||
c479e3fd KC |
250 | crashtype = find_crashtype(buf); |
251 | free_page((unsigned long)buf); | |
0347af4e | 252 | |
c479e3fd | 253 | if (!crashtype) |
0347af4e SK |
254 | return -EINVAL; |
255 | ||
c479e3fd | 256 | err = lkdtm_register_cpoint(crashpoint, crashtype); |
0347af4e SK |
257 | if (err < 0) |
258 | return err; | |
259 | ||
260 | *off += count; | |
261 | ||
262 | return count; | |
263 | } | |
2b271cb7 | 264 | #endif |
0347af4e SK |
265 | |
266 | /* Generic read callback that just prints out the available crash types */ | |
267 | static ssize_t lkdtm_debugfs_read(struct file *f, char __user *user_buf, | |
268 | size_t count, loff_t *off) | |
269 | { | |
73f62e60 KC |
270 | int n, cat, idx; |
271 | ssize_t out; | |
0347af4e | 272 | char *buf; |
0347af4e SK |
273 | |
274 | buf = (char *)__get_free_page(GFP_KERNEL); | |
086ff4b3 AC |
275 | if (buf == NULL) |
276 | return -ENOMEM; | |
0347af4e | 277 | |
87bf65bc | 278 | n = scnprintf(buf, PAGE_SIZE, "Available crash types:\n"); |
73f62e60 KC |
279 | |
280 | for (cat = 0; cat < ARRAY_SIZE(crashtype_categories); cat++) { | |
281 | for (idx = 0; idx < crashtype_categories[cat]->len; idx++) { | |
282 | struct crashtype *crashtype; | |
283 | ||
284 | crashtype = &crashtype_categories[cat]->crashtypes[idx]; | |
285 | n += scnprintf(buf + n, PAGE_SIZE - n, "%s\n", | |
286 | crashtype->name); | |
287 | } | |
c479e3fd | 288 | } |
0347af4e SK |
289 | buf[n] = '\0'; |
290 | ||
291 | out = simple_read_from_buffer(user_buf, count, off, | |
292 | buf, n); | |
293 | free_page((unsigned long) buf); | |
294 | ||
295 | return out; | |
296 | } | |
297 | ||
298 | static int lkdtm_debugfs_open(struct inode *inode, struct file *file) | |
299 | { | |
300 | return 0; | |
301 | } | |
302 | ||
0347af4e SK |
303 | /* Special entry to just crash directly. Available without KPROBEs */ |
304 | static ssize_t direct_entry(struct file *f, const char __user *user_buf, | |
305 | size_t count, loff_t *off) | |
306 | { | |
75f98b7a | 307 | const struct crashtype *crashtype; |
0347af4e | 308 | char *buf; |
84022cff | 309 | int err; |
0347af4e SK |
310 | |
311 | if (count >= PAGE_SIZE) | |
312 | return -EINVAL; | |
313 | if (count < 1) | |
314 | return -EINVAL; | |
315 | ||
316 | buf = (char *)__get_free_page(GFP_KERNEL); | |
317 | if (!buf) | |
318 | return -ENOMEM; | |
319 | if (copy_from_user(buf, user_buf, count)) { | |
320 | free_page((unsigned long) buf); | |
321 | return -EFAULT; | |
322 | } | |
323 | /* NULL-terminate and remove enter */ | |
324 | buf[count] = '\0'; | |
325 | strim(buf); | |
326 | ||
c479e3fd | 327 | crashtype = find_crashtype(buf); |
0347af4e | 328 | free_page((unsigned long) buf); |
c479e3fd | 329 | if (!crashtype) |
0347af4e SK |
330 | return -EINVAL; |
331 | ||
c479e3fd | 332 | pr_info("Performing direct entry %s\n", crashtype->name); |
84022cff | 333 | err = lkdtm_do_action(crashtype); |
0347af4e SK |
334 | *off += count; |
335 | ||
84022cff DA |
336 | if (err) |
337 | return err; | |
0347af4e SK |
338 | return count; |
339 | } | |
340 | ||
5b777131 KC |
341 | #ifndef MODULE |
342 | /* | |
343 | * To avoid needing to export parse_args(), just don't use this code | |
344 | * when LKDTM is built as a module. | |
345 | */ | |
346 | struct check_cmdline_args { | |
347 | const char *param; | |
348 | int value; | |
349 | }; | |
350 | ||
351 | static int lkdtm_parse_one(char *param, char *val, | |
352 | const char *unused, void *arg) | |
353 | { | |
354 | struct check_cmdline_args *args = arg; | |
355 | ||
356 | /* short circuit if we already found a value. */ | |
357 | if (args->value != -ESRCH) | |
358 | return 0; | |
359 | if (strncmp(param, args->param, strlen(args->param)) == 0) { | |
360 | bool bool_result; | |
361 | int ret; | |
362 | ||
363 | ret = kstrtobool(val, &bool_result); | |
364 | if (ret == 0) | |
365 | args->value = bool_result; | |
366 | } | |
367 | return 0; | |
368 | } | |
369 | ||
370 | int lkdtm_check_bool_cmdline(const char *param) | |
371 | { | |
372 | char *command_line; | |
373 | struct check_cmdline_args args = { | |
374 | .param = param, | |
375 | .value = -ESRCH, | |
376 | }; | |
377 | ||
378 | command_line = kstrdup(saved_command_line, GFP_KERNEL); | |
379 | if (!command_line) | |
380 | return -ENOMEM; | |
381 | ||
382 | parse_args("Setting sysctl args", command_line, | |
383 | NULL, 0, -1, -1, &args, lkdtm_parse_one); | |
384 | ||
385 | kfree(command_line); | |
386 | ||
387 | return args.value; | |
388 | } | |
389 | #endif | |
390 | ||
0347af4e SK |
391 | static struct dentry *lkdtm_debugfs_root; |
392 | ||
393 | static int __init lkdtm_module_init(void) | |
394 | { | |
c479e3fd | 395 | struct crashpoint *crashpoint = NULL; |
75f98b7a | 396 | const struct crashtype *crashtype = NULL; |
65bbdd49 | 397 | int ret; |
0347af4e SK |
398 | int i; |
399 | ||
c479e3fd KC |
400 | /* Neither or both of these need to be set */ |
401 | if ((cpoint_type || cpoint_name) && !(cpoint_type && cpoint_name)) { | |
402 | pr_err("Need both cpoint_type and cpoint_name or neither\n"); | |
403 | return -EINVAL; | |
404 | } | |
405 | ||
406 | if (cpoint_type) { | |
407 | crashtype = find_crashtype(cpoint_type); | |
408 | if (!crashtype) { | |
409 | pr_err("Unknown crashtype '%s'\n", cpoint_type); | |
410 | return -EINVAL; | |
411 | } | |
412 | } | |
413 | ||
414 | if (cpoint_name) { | |
415 | for (i = 0; i < ARRAY_SIZE(crashpoints); i++) { | |
416 | if (!strcmp(cpoint_name, crashpoints[i].name)) | |
417 | crashpoint = &crashpoints[i]; | |
418 | } | |
419 | ||
420 | /* Refuse unknown crashpoints. */ | |
421 | if (!crashpoint) { | |
422 | pr_err("Invalid crashpoint %s\n", cpoint_name); | |
423 | return -EINVAL; | |
424 | } | |
425 | } | |
426 | ||
2b271cb7 | 427 | #ifdef CONFIG_KPROBES |
c479e3fd KC |
428 | /* Set crash count. */ |
429 | crash_count = cpoint_count; | |
2b271cb7 | 430 | #endif |
c479e3fd | 431 | |
3a3a11e6 KC |
432 | /* Common initialization. */ |
433 | lkdtm_kernel_info = kasprintf(GFP_KERNEL, "kernel (%s %s)", | |
434 | init_uts_ns.name.release, | |
435 | init_uts_ns.name.machine); | |
436 | ||
a3dff71c | 437 | /* Handle test-specific initialization. */ |
00f496c4 | 438 | lkdtm_bugs_init(&recur_count); |
0d9eb29b | 439 | lkdtm_perms_init(); |
a3dff71c | 440 | lkdtm_usercopy_init(); |
966fede8 | 441 | lkdtm_heap_init(); |
a3dff71c | 442 | |
0347af4e SK |
443 | /* Register debugfs interface */ |
444 | lkdtm_debugfs_root = debugfs_create_dir("provoke-crash", NULL); | |
0347af4e | 445 | |
c479e3fd KC |
446 | /* Install debugfs trigger files. */ |
447 | for (i = 0; i < ARRAY_SIZE(crashpoints); i++) { | |
448 | struct crashpoint *cur = &crashpoints[i]; | |
0347af4e | 449 | |
5a2338db GKH |
450 | debugfs_create_file(cur->name, 0644, lkdtm_debugfs_root, cur, |
451 | &cur->fops); | |
0347af4e SK |
452 | } |
453 | ||
c479e3fd KC |
454 | /* Install crashpoint if one was selected. */ |
455 | if (crashpoint) { | |
456 | ret = lkdtm_register_cpoint(crashpoint, crashtype); | |
0347af4e | 457 | if (ret < 0) { |
c479e3fd | 458 | pr_info("Invalid crashpoint %s\n", crashpoint->name); |
0347af4e SK |
459 | goto out_err; |
460 | } | |
feac6e21 | 461 | pr_info("Crash point %s of type %s registered\n", |
c479e3fd | 462 | crashpoint->name, cpoint_type); |
0347af4e | 463 | } else { |
feac6e21 | 464 | pr_info("No crash points registered, enable through debugfs\n"); |
8bb31b9d AG |
465 | } |
466 | ||
8bb31b9d | 467 | return 0; |
0347af4e SK |
468 | |
469 | out_err: | |
470 | debugfs_remove_recursive(lkdtm_debugfs_root); | |
471 | return ret; | |
8bb31b9d AG |
472 | } |
473 | ||
2118116e | 474 | static void __exit lkdtm_module_exit(void) |
8bb31b9d | 475 | { |
0347af4e SK |
476 | debugfs_remove_recursive(lkdtm_debugfs_root); |
477 | ||
a3dff71c | 478 | /* Handle test-specific clean-up. */ |
966fede8 | 479 | lkdtm_heap_exit(); |
a3dff71c | 480 | lkdtm_usercopy_exit(); |
aa981a66 | 481 | |
31c5c870 KC |
482 | if (lkdtm_kprobe != NULL) |
483 | unregister_kprobe(lkdtm_kprobe); | |
9ba60573 | 484 | |
3a3a11e6 KC |
485 | kfree(lkdtm_kernel_info); |
486 | ||
feac6e21 | 487 | pr_info("Crash point unregistered\n"); |
8bb31b9d AG |
488 | } |
489 | ||
490 | module_init(lkdtm_module_init); | |
491 | module_exit(lkdtm_module_exit); | |
492 | ||
493 | MODULE_LICENSE("GPL"); | |
c479e3fd | 494 | MODULE_DESCRIPTION("Kernel crash testing module"); |