Message ID | 20221109200050.3400857-2-keescook@chromium.org (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | exit: Put an upper limit on how often we can oops | expand |
Hi Kees, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on next-20221109] url: https://github.com/intel-lab-lkp/linux/commits/Kees-Cook/exit-Put-an-upper-limit-on-how-often-we-can-oops/20221110-040244 patch link: https://lore.kernel.org/r/20221109200050.3400857-2-keescook%40chromium.org patch subject: [PATCH v2 2/6] exit: Put an upper limit on how often we can oops config: m68k-randconfig-r024-20221111 compiler: m68k-linux-gcc (GCC) 12.1.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/intel-lab-lkp/linux/commit/3db52f787eeec7924f3c2f952ac8fdf33b70b0bf git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kees-Cook/exit-Put-an-upper-limit-on-how-often-we-can-oops/20221110-040244 git checkout 3db52f787eeec7924f3c2f952ac8fdf33b70b0bf # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=m68k SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> All warnings (new ones prefixed by >>): >> kernel/exit.c:82:5: warning: "CONFIG_SYSCTL" is not defined, evaluates to 0 [-Wundef] 82 | #if CONFIG_SYSCTL | ^~~~~~~~~~~~~ kernel/exit.c:1881:13: warning: no previous prototype for 'abort' [-Wmissing-prototypes] 1881 | __weak void abort(void) | ^~~~~ vim +/CONFIG_SYSCTL +82 kernel/exit.c 81 > 82 #if CONFIG_SYSCTL 83 static struct ctl_table kern_exit_table[] = { 84 { 85 .procname = "oops_limit", 86 .data = &oops_limit, 87 .maxlen = sizeof(oops_limit), 88 .mode = 0644, 89 .proc_handler = proc_douintvec, 90 }, 91 { } 92 }; 93
Hi Kees, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on next-20221109] url: https://github.com/intel-lab-lkp/linux/commits/Kees-Cook/exit-Put-an-upper-limit-on-how-often-we-can-oops/20221110-040244 patch link: https://lore.kernel.org/r/20221109200050.3400857-2-keescook%40chromium.org patch subject: [PATCH v2 2/6] exit: Put an upper limit on how often we can oops config: riscv-randconfig-s053-20221113 compiler: riscv32-linux-gcc (GCC) 12.1.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # apt-get install sparse # sparse version: v0.6.4-39-gce1a6720-dirty # https://github.com/intel-lab-lkp/linux/commit/3db52f787eeec7924f3c2f952ac8fdf33b70b0bf git remote add linux-review https://github.com/intel-lab-lkp/linux git fetch --no-tags linux-review Kees-Cook/exit-Put-an-upper-limit-on-how-often-we-can-oops/20221110-040244 git checkout 3db52f787eeec7924f3c2f952ac8fdf33b70b0bf # save the config file mkdir build_dir && cp config build_dir/.config COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=riscv SHELL=/bin/bash If you fix the issue, kindly add following tag where applicable | Reported-by: kernel test robot <lkp@intel.com> sparse warnings: (new ones prefixed by >>) WARNING: invalid argument to '-march': '_zihintpause' >> kernel/exit.c:82:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_SYSCTL' kernel/exit.c:313:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *tsk @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:313:37: sparse: expected struct task_struct *tsk kernel/exit.c:313:37: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:316:32: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *task @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:316:32: sparse: expected struct task_struct *task kernel/exit.c:316:32: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:317:35: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *task @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:317:35: sparse: expected struct task_struct *task kernel/exit.c:317:35: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:362:24: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct task_struct *parent @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:362:24: sparse: expected struct task_struct *parent kernel/exit.c:362:24: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:389:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:389:27: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:389:27: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:392:29: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:392:29: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:392:29: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:616:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct task_struct *reaper @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:616:29: sparse: expected struct task_struct *reaper kernel/exit.c:616:29: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:618:29: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct task_struct *reaper @@ got struct task_struct [noderef] __rcu *real_parent @@ kernel/exit.c:618:29: sparse: expected struct task_struct *reaper kernel/exit.c:618:29: sparse: got struct task_struct [noderef] __rcu *real_parent kernel/exit.c:771:45: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct sighand_struct *sighand @@ got struct sighand_struct [noderef] __rcu *sighand @@ kernel/exit.c:771:45: sparse: expected struct sighand_struct *sighand kernel/exit.c:771:45: sparse: got struct sighand_struct [noderef] __rcu *sighand kernel/exit.c:976:63: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct sighand_struct *const sighand @@ got struct sighand_struct [noderef] __rcu *sighand @@ kernel/exit.c:976:63: sparse: expected struct sighand_struct *const sighand kernel/exit.c:976:63: sparse: got struct sighand_struct [noderef] __rcu *sighand kernel/exit.c:1131:39: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1131:39: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1131:39: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1156:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1156:41: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1156:41: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1245:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1245:25: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1245:25: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1260:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1260:27: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1260:27: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1311:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1311:25: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1311:25: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1314:35: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1314:35: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1314:35: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1320:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct spinlock [usertype] *lock @@ got struct spinlock [noderef] __rcu * @@ kernel/exit.c:1320:27: sparse: expected struct spinlock [usertype] *lock kernel/exit.c:1320:27: sparse: got struct spinlock [noderef] __rcu * kernel/exit.c:1501:59: sparse: sparse: incompatible types in comparison expression (different base types): kernel/exit.c:1501:59: sparse: void * kernel/exit.c:1501:59: sparse: struct task_struct [noderef] __rcu * kernel/exit.c:1517:25: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected struct task_struct *parent @@ got struct task_struct [noderef] __rcu * @@ kernel/exit.c:1517:25: sparse: expected struct task_struct *parent kernel/exit.c:1517:25: sparse: got struct task_struct [noderef] __rcu * kernel/exit.c: note: in included file: include/linux/ptrace.h:92:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p1 @@ got struct task_struct [noderef] __rcu *real_parent @@ include/linux/ptrace.h:92:40: sparse: expected struct task_struct *p1 include/linux/ptrace.h:92:40: sparse: got struct task_struct [noderef] __rcu *real_parent include/linux/ptrace.h:92:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct task_struct *p2 @@ got struct task_struct [noderef] __rcu *parent @@ include/linux/ptrace.h:92:60: sparse: expected struct task_struct *p2 include/linux/ptrace.h:92:60: sparse: got struct task_struct [noderef] __rcu *parent include/linux/ptrace.h:92:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p1 @@ got struct task_struct [noderef] __rcu *real_parent @@ include/linux/ptrace.h:92:40: sparse: expected struct task_struct *p1 include/linux/ptrace.h:92:40: sparse: got struct task_struct [noderef] __rcu *real_parent include/linux/ptrace.h:92:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct task_struct *p2 @@ got struct task_struct [noderef] __rcu *parent @@ include/linux/ptrace.h:92:60: sparse: expected struct task_struct *p2 include/linux/ptrace.h:92:60: sparse: got struct task_struct [noderef] __rcu *parent kernel/exit.c: note: in included file (through include/linux/sched/signal.h, include/linux/rcuwait.h, include/linux/percpu-rwsem.h, ...): include/linux/sched/task.h:110:21: sparse: sparse: context imbalance in 'wait_task_zombie' - unexpected unlock include/linux/sched/task.h:110:21: sparse: sparse: context imbalance in 'wait_task_stopped' - unexpected unlock include/linux/sched/task.h:110:21: sparse: sparse: context imbalance in 'wait_task_continued' - unexpected unlock kernel/exit.c: note: in included file: include/linux/ptrace.h:92:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected struct task_struct *p1 @@ got struct task_struct [noderef] __rcu *real_parent @@ include/linux/ptrace.h:92:40: sparse: expected struct task_struct *p1 include/linux/ptrace.h:92:40: sparse: got struct task_struct [noderef] __rcu *real_parent include/linux/ptrace.h:92:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@ expected struct task_struct *p2 @@ got struct task_struct [noderef] __rcu *parent @@ include/linux/ptrace.h:92:60: sparse: expected struct task_struct *p2 include/linux/ptrace.h:92:60: sparse: got struct task_struct [noderef] __rcu *parent kernel/exit.c: note: in included file (through include/linux/thread_info.h, include/asm-generic/preempt.h, arch/riscv/include/generated/asm/preempt.h, ...): arch/riscv/include/asm/current.h:31:9: sparse: sparse: context imbalance in 'do_wait' - wrong count at exit vim +/CONFIG_SYSCTL +82 kernel/exit.c 81 > 82 #if CONFIG_SYSCTL 83 static struct ctl_table kern_exit_table[] = { 84 { 85 .procname = "oops_limit", 86 .data = &oops_limit, 87 .maxlen = sizeof(oops_limit), 88 .mode = 0644, 89 .proc_handler = proc_douintvec, 90 }, 91 { } 92 }; 93
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index 98d1b198b2b4..09f3fb2f8585 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -667,6 +667,14 @@ This is the default behavior. an oops event is detected. +oops_limit +========== + +Number of kernel oopses after which the kernel should panic when +``panic_on_oops`` is not set. Setting this to 0 or 1 has the same effect +as setting ``panic_on_oops=1``. + + osrelease, ostype & version =========================== diff --git a/kernel/exit.c b/kernel/exit.c index 35e0a31a0315..892f38aeb0a4 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -72,6 +72,33 @@ #include <asm/unistd.h> #include <asm/mmu_context.h> +/* + * The default value should be high enough to not crash a system that randomly + * crashes its kernel from time to time, but low enough to at least not permit + * overflowing 32-bit refcounts or the ldsem writer count. + */ +static unsigned int oops_limit = 10000; + +#if CONFIG_SYSCTL +static struct ctl_table kern_exit_table[] = { + { + .procname = "oops_limit", + .data = &oops_limit, + .maxlen = sizeof(oops_limit), + .mode = 0644, + .proc_handler = proc_douintvec, + }, + { } +}; + +static __init int kernel_exit_sysctls_init(void) +{ + register_sysctl_init("kernel", kern_exit_table); + return 0; +} +late_initcall(kernel_exit_sysctls_init); +#endif + static void __unhash_process(struct task_struct *p, bool group_dead) { nr_threads--; @@ -874,6 +901,8 @@ void __noreturn do_exit(long code) void __noreturn make_task_dead(int signr) { + static atomic_t oops_count = ATOMIC_INIT(0); + /* * Take the task off the cpu after something catastrophic has * happened. @@ -897,6 +926,19 @@ void __noreturn make_task_dead(int signr) preempt_count_set(PREEMPT_ENABLED); } + /* + * Every time the system oopses, if the oops happens while a reference + * to an object was held, the reference leaks. + * If the oops doesn't also leak memory, repeated oopsing can cause + * reference counters to wrap around (if they're not using refcount_t). + * This means that repeated oopsing can make unexploitable-looking bugs + * exploitable through repeated oopsing. + * To make sure this can't happen, place an upper bound on how often the + * kernel may oops without panic(). + */ + if (atomic_inc_return(&oops_count) >= READ_ONCE(oops_limit)) + panic("Oopsed too often (oops_limit is %d)", oops_limit); + /* * We're taking recursive faults here in make_task_dead. Safest is to just * leave this task alone and wait for reboot.