Linux 宕機驅動
阿新 • • 發佈:2020-12-23
原理:通過建立proc虛擬檔案來與核心互動,互動中通過訪問空指標,只讀記憶體,重複釋放記憶體,死鎖等方式來達到系統宕機或卡死的狀態。
一般用來測試系統硬狗。
#include <linux/module.h> #include <linux/sched.h> #include <linux/fs.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/uaccess.h> #include <linux/sysctl.h> #include <linux/list.h> #include <linux/highmem.h> #include <asm/uaccess.h> #include <linux/vmalloc.h> #include <linux/netdevice.h> #include <asm/cacheflush.h> #include <linux/spinlock_types.h> #define DEAD_MAGIC (0xadde78563412addeUL) #define CMD_PARAM_LEN (64) #define MEM_UNIT (1024 * 1024 * 10) #define MAX_BLOCK_CNT (3200) //#define __RAW_SPIN_LOCK_UNLOCKED { } #define SPIN_DEP_MAP_INIT(lockname) #define __RAW_RW_LOCK_UNLOCKED { 0 } #define RW_DEP_MAP_INIT(lockname) static void *s_pointer[MAX_BLOCK_CNT] = {NULL}; static int s_index = 0; typedef void (*crash_func_t)(unsigned long data); struct cmd_pair_st { const char *cmd_name; crash_func_t func; const char *cmd_desc; int is_need_cpuid; }; struct cmd_param_st { char param1[CMD_PARAM_LEN]; char param2[CMD_PARAM_LEN]; }; /* #ifdef CONFIG_DEBUG_SPINLOCK #define __SPIN_LOCK_UNLOCKED(lockname) \ (spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED, \ .magic = SPINLOCK_MAGIC, \ .owner = SPINLOCK_OWNER_INIT, \ .owner_cpu = -1, \ SPIN_DEP_MAP_INIT(lockname) } #define __RW_LOCK_UNLOCKED(lockname) \ (rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED, \ .magic = RWLOCK_MAGIC, \ .owner = SPINLOCK_OWNER_INIT, \ .owner_cpu = -1, \ RW_DEP_MAP_INIT(lockname) } #else #define __SPIN_LOCK_UNLOCKED(lockname) \ (spinlock_t) { .raw_lock = __RAW_SPIN_LOCK_UNLOCKED, \ SPIN_DEP_MAP_INIT(lockname) } #define __RW_LOCK_UNLOCKED(lockname) \ (rwlock_t) { .raw_lock = __RAW_RW_LOCK_UNLOCKED, \ RW_DEP_MAP_INIT(lockname) } #endif */ //#define SPIN_LOCK_UNLOCKED __SPIN_LOCK_UNLOCKED(old_style_spin_init) #define DEFINE_SPINLOCK(x) spinlock_t x = __SPIN_LOCK_UNLOCKED(x) #define dbg(text, par...) \ do { \ printk(KERN_EMERG " [%s:%d] "text, __FILE__, __LINE__, ##par); \ } while(0) static void null_ptr_func(unsigned long data) { *(unsigned long*)0 = DEAD_MAGIC; } static struct timer_list null_ptr_timer; static void null_ptr_timer_func(unsigned long data) { dbg("null_ptr_timer_func in\n"); *(unsigned long*)0 = DEAD_MAGIC; } static void timer_null_ptr_func(unsigned long data) { setup_timer(&null_ptr_timer, null_ptr_timer_func, 0); null_ptr_timer.expires = jiffies + HZ; add_timer(&null_ptr_timer); dbg("null_ptr timer started\n"); } static void write_readonly_func(unsigned long data) { void *addr = (void*)__get_free_page(GFP_KERNEL); if (!addr) { dbg("vmalloc failed\n"); return; } dbg("page addr:%p\n", addr); free_page((unsigned long)addr); /*需要開啟核心空閒頁只讀檢測功能*/ *(unsigned long*)addr = DEAD_MAGIC; } static void recursive_overflow(int level, void *data) { unsigned long local_data = DEAD_MAGIC; dbg("recursive depth=%d\n", level); if (level > 1024) return; recursive_overflow(level + 1, &local_data); *(unsigned long*)data = local_data; } static void stack_overflow_func(unsigned long data) { unsigned long local_data; recursive_overflow(0, &local_data); } static void read_overflow_func(unsigned long data) { dbg("do nothing!\n"); } static void double_kfree_func(unsigned long data) { void *addr = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!addr) { dbg("kmalloc failed\n"); return; } dbg("addr=%p\n", addr); kfree(addr); kfree(addr); } static void double_sfree_func(unsigned long data) { void *addr; struct kmem_cache *test_cache; test_cache = kmem_cache_create("double_sfree_test", 128, 0, SLAB_HWCACHE_ALIGN, NULL); if (!test_cache) { dbg("kmem_cache_create failed\n"); return; } addr = kmem_cache_alloc(test_cache, GFP_KERNEL); if (!addr) { dbg("kmem_cache_alloc failed\n"); return; } dbg("addr=%p\n", addr); kmem_cache_free(test_cache, addr); kmem_cache_free(test_cache, addr); kmem_cache_destroy(test_cache); } static void double_vfree_func(unsigned long data) { void *addr = vmalloc(PAGE_SIZE); if (!addr) { dbg("vmalloc failed\n"); return; } dbg("addr=%p\n", addr); vfree(addr); vfree(addr); } static void double_pfree_func(unsigned long data) { void *addr = (void*)__get_free_page(GFP_KERNEL); if (!addr) { dbg("vmalloc failed\n"); return; } dbg("addr=%p\n", addr); free_page((unsigned long)addr); free_page((unsigned long)addr); } static DEFINE_SPINLOCK(sleeplock); static void lock_sleep_func(unsigned long data) { dbg("spin lock"); spin_lock(&sleeplock); msleep(HZ); spin_unlock(&sleeplock); dbg("spin unlock"); } static void bh_sleep_func(unsigned long data) { dbg("local_bh_disable"); local_bh_disable(); msleep(HZ); local_bh_enable(); dbg("local_bh_enable"); } static struct timer_list sleep_timer; static void sleep_timer_func(unsigned long data) { dbg("sleep_timer_func in\n"); msleep(HZ); } static void timer_sleep_func(unsigned long data) { setup_timer(&sleep_timer, sleep_timer_func, 0); sleep_timer.expires = jiffies + HZ; add_timer(&sleep_timer); dbg("sleep timer started\n"); } static void irq_sleep_func(unsigned long data) { dbg("local_irq_disable"); local_irq_disable(); msleep(HZ); local_irq_enable(); dbg("local_irq_enable"); } static DEFINE_SPINLOCK(deadlock); static void kernel_deadlock_func(unsigned long data) { dbg("spin_lock"); spin_lock(&deadlock); dbg("spin_lock again"); spin_lock(&deadlock); } static void bh_deadlock_func(unsigned long data) { dbg("spin_lock_bh"); spin_lock_bh(&deadlock); dbg("spin_lock_bh again"); spin_lock_bh(&deadlock); } static struct timer_list dead_timer; static void dead_timer_func(unsigned long data) { dbg("dead_timer_func in\n"); spin_lock(&deadlock); spin_lock(&deadlock); } static void timer_deadlock_func(unsigned long data) { setup_timer(&dead_timer, dead_timer_func, 0); dead_timer.expires = jiffies + HZ; add_timer(&dead_timer); dbg("dead timer started\n"); } static void irq_deadlock_func(unsigned long data) { dbg("spin_lock_irq\n"); spin_lock_irq(&deadlock); dbg("spin_lock_irq again"); spin_lock_irq(&deadlock); } static void bh_kmalloc_func(unsigned long data) { void *mem; local_bh_disable(); mem = kmalloc(PAGE_SIZE, GFP_KERNEL); if (mem) kfree(mem); else printk("kmalloc failed\n"); local_bh_enable(); } static void bh_kmem_func(unsigned long data) { void *mem; struct kmem_cache *test_cache; test_cache = kmem_cache_create("test_cache", 100, 0, 0, NULL); if (!test_cache) { printk("kmem_cache_create failed\n"); return; } local_bh_disable(); mem = kmem_cache_alloc(test_cache, GFP_KERNEL); if (mem) kmem_cache_free(test_cache, mem); else printk("kmem_cache_alloc failed\n"); local_bh_enable(); kmem_cache_destroy(test_cache); } static void eat_mem_func(unsigned long data) { int i = 0, count = 0; struct cmd_param_st *param = (struct cmd_param_st *)data; if (param && param->param1[0] != '0') count = simple_strtol(param->param1, NULL, 0); for (; i < count && i < MAX_BLOCK_CNT; i++) { if (s_pointer[s_index] == NULL) s_pointer[s_index++] = vmalloc(MEM_UNIT); } } static void free_mem_func(unsigned long data) { int i = 0; for (; i < MAX_BLOCK_CNT; i++) { if (s_pointer[i] != NULL) { vfree(s_pointer[i]); s_pointer[i] = NULL; } } } static void bh_vmalloc_func(unsigned long data) { void *mem; dbg("local_bh_disable"); local_bh_disable(); mem = vmalloc(PAGE_SIZE); dbg("mem=%p", mem); if (mem) vfree(mem); else dbg("vmalloc failed\n"); dbg("local_bh_enable"); local_bh_enable(); } struct cmd_pair_st cmd_suit[] = { {"null_ptr", null_ptr_func, "[need cpu id]", 1}, {"timer_null_ptr", timer_null_ptr_func, "[need cpu id]", 1}, {"write_readonly", write_readonly_func, "[need cpu id]", 1}, {"stack_overflow", stack_overflow_func, "[need cpu id]", 1}, {"read_overflow", read_overflow_func, "[need cpu id]", 1}, {"double_kfree", double_kfree_func, "[need cpu id]", 1}, {"double_sfree", double_sfree_func, "[need cpu id]", 1}, {"double_vfree", double_vfree_func, "[need cpu id]", 1}, {"double_pfree", double_pfree_func, "[need cpu id]", 1}, {"lock_sleep", lock_sleep_func, "[need cpu id]", 1}, {"bh_sleep", bh_sleep_func, "[need cpu id]", 1}, {"timer_sleep", timer_sleep_func, "[need cpu id]", 1}, {"irq_sleep", irq_sleep_func, "[need cpu id]", 1}, {"kernel_deadlock", kernel_deadlock_func, "[need cpu id]", 1}, {"bh_deadlock", bh_deadlock_func, "[need cpu id]", 1}, {"timer_deadlock", timer_deadlock_func, "[need cpu id]", 1}, {"irq_deadlock", irq_deadlock_func, "[need cpu id]", 1}, {"bh_vmalloc", bh_vmalloc_func, "[need cpu id]", 1}, {"bh_kmalloc_kernel", bh_kmalloc_func, "[need cpu id]", 1}, {"bh_kmem_kernel", bh_kmem_func, "[need cpu id]", 1}, {"eat_mem", eat_mem_func, "need param:count(unit:10M)", 0}, {"free_mem", free_mem_func, "no need param", 0} }; static ssize_t test_crash_read(struct file * file, char * buf, size_t count, loff_t *ppos) { int i; int len = 0; static char tmp_buf[PAGE_SIZE]; int mylen = sizeof(tmp_buf); if (!buf || count < 512) return -1; if (*ppos != 0) return 0; len += snprintf(tmp_buf + len, mylen - len, "cmd: echo cmd > /proc/test_crash\n"); len += snprintf(tmp_buf + len, mylen - len, "available cmds:\n"); for (i = 0; i < ARRAY_SIZE(cmd_suit); i++) { if (cmd_suit[i].cmd_desc) { len += snprintf(tmp_buf + len, mylen - len, " echo %-20s >/proc/test_crash # %-40s\n", cmd_suit[i].cmd_name, cmd_suit[i].cmd_desc); } else { len += snprintf(tmp_buf + len, mylen - len, " echo %-20s >/proc/test_crash\n", cmd_suit[i].cmd_name); } } len = min(len, (int)count); if (copy_to_user(buf, tmp_buf, len)) { dbg("test_crash_read, copy_from_user failed\n"); } *ppos += len; return len; } static struct cmd_pair_st *find_cmd_pair(const char *cmd_name) { int i; for (i = 0; i < ARRAY_SIZE(cmd_suit); i++) { if (strcmp(cmd_name, cmd_suit[i].cmd_name) == 0) { return &cmd_suit[i]; } } return NULL; } static void bind_cpu_crash(crash_func_t func, struct cmd_param_st *param) { int cpuid = 0; if (param->param1[0] != '0') cpuid = simple_strtol(param->param1, NULL, 0); dbg("current cpu id:%d\n", cpuid); setup_timer(&dead_timer, func, 0); dead_timer.expires = jiffies + HZ; add_timer_on(&dead_timer, cpuid); dbg("dead timer started\n"); } static ssize_t test_crash_write(struct file * file, const char * buf, size_t count, loff_t *ppos) { #define CMD_BUFF_SIZE (256) int len; int ret; char cmd_buf[CMD_BUFF_SIZE+1]; char cmd_name[64] = {0}; struct cmd_pair_st *cmd; static struct cmd_param_st param = {{0},{0}}; len = min((int)count, CMD_BUFF_SIZE); if (copy_from_user(cmd_buf, buf, len)) { dbg("copy_from_user failed, len=%d\n", len); return -1; } cmd_buf[len] = 0; ret = sscanf(cmd_buf, "%63s %63s", cmd_name, param.param1); if (ret != 2) { dbg("now cmd: %s\n", cmd_buf); } cmd = find_cmd_pair(cmd_name); if (cmd && cmd->func) { dbg("start call crash. name:%s, param:%s\n", cmd->cmd_name, cmd_buf); if (ret == 1 || cmd->is_need_cpuid == 0) { cmd->func((unsigned long)¶m); } else { bind_cpu_crash(cmd->func, ¶m); } } return count; } static struct file_operations test_crash_ops = { .read = test_crash_read, .write = test_crash_write, }; static int __init test_crash_init(void) { struct proc_dir_entry* file; //建立proc檔案並關聯file_operations file = proc_create("test_crash", S_IRUGO|S_IWUGO/*0644*/, NULL , &test_crash_ops); if (!file) return -ENOMEM; dbg("test_crash_init ok\n"); dbg("use \'cat /proc/test_crash' to show usage!\n"); return 0; } static void __exit test_crash_fini(void) { remove_proc_entry("test_crash", NULL); dbg("test_crash_fini ok\n"); } module_init(test_crash_init); module_exit(test_crash_fini); MODULE_AUTHOR("flz"); MODULE_LICENSE("GPL");
Makefile:
TARGET = test_crash.ko KERNEL_SRC_DIR=/home/flz/openwrt/build_dir/target-x86_64_musl/linux-x86_64/linux-4.14.180 TOOLCHAIN=x86_64-openwrt-linux-musl- STAGING_DIR=/home/flz/openwrt/staging_dir/target-x86_64_musl obj-m += test_crash.o all: make -C $(KERNEL_SRC_DIR) M=$(PWD) ARCH=x86 CROSS_COMPILE=$(TOOLCHAIN) modules clean: rm -f *.o Module.symvers modules.order *.mod* distclean:clean rm *.ko
通過echo 0 > /sys/proc/kernel/panic 來關閉系統panic
使用方法:
裝載驅動:insmod test_crash.ko
檢視可用的宕機方法:
cat /prce/test_crash
例如:
echo null_ptr 0 > /proc/test_crash
echo lock_sleep 1 > /proc/test_crash
上上面的0,1指的是cpu id