Skip to contentSkip to conversation

Backport oops_limit to 4.14

PATCH Applied Eric Biggers 3 participants 18 messages Feb 3, 2023

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v2 cover Backport oops_limit to 4.14

This series backports the patchset
"exit: Put an upper limit on how often we can oops"
(https://lore.kernel.org/linux-mm/[email protected]/T/#u)
to 4.14, as recommended at
https://googleprojectzero.blogspot.com/2023/01/exploiting-null-dereferences-in-linux.html

Changed in v2:
- Fixed a build error in mm/kasan/report.c by dropping the patch "mm:
kasan: do not panic if both panic_on_warn and kasan_multishot set".

Eric W. Biederman (2):
exit: Add and use make_task_dead.
objtool: Add a missing comma to avoid string concatenation

Jann Horn (1):
exit: Put an upper limit on how often we can oops

Kees Cook (7):
exit: Expose "oops_count" to sysfs
exit: Allow oops_limit to be disabled
panic: Consolidate open-coded panic_on_warn checks
panic: Introduce warn_limit
panic: Expose "warn_count" to sysfs
docs: Fix path paste-o for /sys/kernel/warn_count
exit: Use READ_ONCE() for all oops/warn limit reads

Nathan Chancellor (2):
hexagon: Fix function name in die()
h8300: Fix build errors from do_exit() to make_task_dead() transition

Randy Dunlap (1):
ia64: make IA64_MCA_RECOVERY bool instead of tristate

Tiezhu Yang (1):
panic: unset panic_on_warn inside panic()

Xiaoming Ni (1):
sysctl: add a new register_sysctl_init() interface

 .../ABI/testing/sysfs-kernel-oops_count       |  6 ++
 .../ABI/testing/sysfs-kernel-warn_count       |  6 ++
 Documentation/sysctl/kernel.txt               | 20 +++++
 arch/alpha/kernel/traps.c                     |  6 +-
 arch/alpha/mm/fault.c                         |  2 +-
 arch/arm/kernel/traps.c                       |  2 +-
 arch/arm/mm/fault.c                           |  2 +-
 arch/arm64/kernel/traps.c                     |  2 +-
 arch/arm64/mm/fault.c                         |  2 +-
 arch/h8300/kernel/traps.c                     |  3 +-
 arch/h8300/mm/fault.c                         |  2 +-
 arch/hexagon/kernel/traps.c                   |  2 +-
 arch/ia64/Kconfig                             |  2 +-
 arch/ia64/kernel/mca_drv.c                    |  2 +-
 arch/ia64/kernel/traps.c                      |  2 +-
 arch/ia64/mm/fault.c                          |  2 +-
 arch/m68k/kernel/traps.c                      |  2 +-
 arch/m68k/mm/fault.c                          |  2 +-
 arch/microblaze/kernel/exceptions.c           |  4 +-
 arch/mips/kernel/traps.c                      |  2 +-
 arch/nios2/kernel/traps.c                     |  4 +-
 arch/openrisc/kernel/traps.c                  |  2 +-
 arch/parisc/kernel/traps.c                    |  2 +-
 arch/powerpc/kernel/traps.c                   |  2 +-
 arch/s390/kernel/dumpstack.c                  |  2 +-
 arch/s390/kernel/nmi.c                        |  2 +-
 arch/sh/kernel/traps.c                        |  2 +-
 arch/sparc/kernel/traps_32.c                  |  4 +-
 arch/sparc/kernel/traps_64.c                  |  4 +-
 arch/x86/entry/entry_32.S                     |  6 +-
 arch/x86/entry/entry_64.S                     |  6 +-
 arch/x86/kernel/dumpstack.c                   |  4 +-
 arch/xtensa/kernel/traps.c                    |  2 +-
 fs/proc/proc_sysctl.c                         | 33 ++++++++
 include/linux/kernel.h                        |  1 +
 include/linux/sched/task.h                    |  1 +
 include/linux/sysctl.h                        |  3 +
 kernel/exit.c                                 | 72 ++++++++++++++++++
 kernel/panic.c                                | 75 ++++++++++++++++---
 kernel/sched/core.c                           |  3 +-
 mm/kasan/report.c                             |  3 +-
 tools/objtool/check.c                         |  3 +-
 42 files changed, 251 insertions(+), 58 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-oops_count
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-warn_count

base-commit: 3949d16100044eeb6723eca03b1038b469fb9ae8

Signature · 1 line
2.39.1
#1 lore raw

PATCH v2 1/15 sysctl: add a new register_sysctl_init() interface

From: Xiaoming Ni <[email protected]>

commit 3ddd9a808cee7284931312f2f3e854c9617f44b2 upstream.

Patch series "sysctl: first set of kernel/sysctl cleanups", v2.

Finally had time to respin the series of the work we had started last year on cleaning up the kernel/sysct.c kitchen sink. People keeps stuffing their sysctls in that file and this creates a maintenance burden. So this effort is aimed at placing sysctls where they actually belong.

I'm going to split patches up into series as there is quite a bit of work.

This first set adds register_sysctl_init() for uses of registerting a sysctl on the init path, adds const where missing to a few places, generalizes common values so to be more easy to share, and starts the move of a few kernel/sysctl.c out where they belong.

The majority of rework on v2 in this first patch set is 0-day fixes. Eric Biederman's feedback is later addressed in subsequent patch sets.

I'll only post the first two patch sets for now. We can address the rest once the first two patch sets get completely reviewed / Acked.

This patch (of 9):

The kernel/sysctl.c is a kitchen sink where everyone leaves their dirty dishes, this makes it very difficult to maintain.

To help with this maintenance let's start by moving sysctls to places where they actually belong. The proc sysctl maintainers do not want to know what sysctl knobs you wish to add for your own piece of code, we just care about the core logic.

Today though folks heavily rely on tables on kernel/sysctl.c so they can easily just extend this table with their needed sysctls. In order to help users move their sysctls out we need to provide a helper which can be used during code initialization.

We special-case the initialization use of register_sysctl() since it *is* safe to fail, given all that sysctls do is provide a dynamic interface to query or modify at runtime an existing variable. So the use case of register_sysctl() on init should *not* stop if the sysctls don't end up getting registered. It would be counter productive to stop boot if a simple sysctl registration failed.

Provide a helper for init then, and document the recommended init levels to use for callers of this routine. We will later use this in subsequent patches to start slimming down kernel/sysctl.c tables and moving sysctl registration to the code which actually needs these sysctls.

[[email protected]: major commit log and documentation rephrasing also moved to fs/proc/proc_sysctl.c ]

Cc Iurii Zaikin <[email protected]>
Cc "Eric W. Biederman" <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Greg Kroah-Hartman <[email protected]>
Cc Paul Turner <[email protected]>
Cc Andy Shevchenko <[email protected]>
Cc Sebastian Reichel <[email protected]>
Cc Tetsuo Handa <[email protected]>
Cc Petr Mladek <[email protected]>
Cc Sergey Senozhatsky <[email protected]>
Cc Qing Wang <[email protected]>
Cc Benjamin LaHaise <[email protected]>
Cc Al Viro <[email protected]>
Cc Jan Kara <[email protected]>
Cc Amir Goldstein <[email protected]>
Cc Stephen Kitt <[email protected]>
Cc Antti Palosaari <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Benjamin Herrenschmidt <[email protected]>
Cc Clemens Ladisch <[email protected]>
Cc David Airlie <[email protected]>
Cc Jani Nikula <[email protected]>
Cc Joel Becker <[email protected]>
Cc Joonas Lahtinen <[email protected]>
Cc Joseph Qi <[email protected]>
Cc Julia Lawall <[email protected]>
Cc Lukas Middendorf <[email protected]>
Cc Mark Fasheh <[email protected]>
Cc Phillip Potter <[email protected]>
Cc Rodrigo Vivi <[email protected]>
Cc Douglas Gilbert <[email protected]>
Cc James E.J. Bottomley <[email protected]>
Cc Jani Nikula <[email protected]>
Cc John Ogness <[email protected]>
Cc Martin K. Petersen <[email protected]>
Cc "Rafael J. Wysocki" <[email protected]>
Cc Steven Rostedt (VMware) <[email protected]>
Cc Suren Baghdasaryan <[email protected]>
Cc "Theodore Ts'o" <[email protected]>
---
 fs/proc/proc_sysctl.c  | 33 +++++++++++++++++++++++++++++++++
 include/linux/sysctl.h |  3 +++
 2 files changed, 36 insertions(+)
fs/proc/proc_sysctl.c +33 -0
index 12bac452738df..f4b46f796901c 100644
@@ -13,6 +13,7 @@
 #include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <linux/kmemleak.h>
 #include "internal.h"
 
 static const struct dentry_operations proc_sys_dentry_operations;
@@ -1370,6 +1371,38 @@ struct ctl_table_header *register_sysctl(const char *path, struct ctl_table *tab
 }
 EXPORT_SYMBOL(register_sysctl);
 
+/**
+ * __register_sysctl_init() - register sysctl table to path
+ * @path: path name for sysctl base
+ * @table: This is the sysctl table that needs to be registered to the path
+ * @table_name: The name of sysctl table, only used for log printing when
+ *              registration fails
+ *
+ * The sysctl interface is used by userspace to query or modify at runtime
+ * a predefined value set on a variable. These variables however have default
+ * values pre-set. Code which depends on these variables will always work even
+ * if register_sysctl() fails. If register_sysctl() fails you'd just loose the
+ * ability to query or modify the sysctls dynamically at run time. Chances of
+ * register_sysctl() failing on init are extremely low, and so for both reasons
+ * this function does not return any error as it is used by initialization code.
+ *
+ * Context: Can only be called after your respective sysctl base path has been
+ * registered. So for instance, most base directories are registered early on
+ * init before init levels are processed through proc_sys_init() and
+ * sysctl_init().
+ */
+void __init __register_sysctl_init(const char *path, struct ctl_table *table,
+				 const char *table_name)
+{
+	struct ctl_table_header *hdr = register_sysctl(path, table);
+
+	if (unlikely(!hdr)) {
+		pr_err("failed when register_sysctl %s to %s\n", table_name, path);
+		return;
+	}
+	kmemleak_not_leak(hdr);
+}
+
 static char *append_path(const char *path, char *pos, const char *name)
 {
 	int namelen;
include/linux/sysctl.h +3 -0
index b769ecfcc3bd4..0a980aecc8f02 100644
@@ -198,6 +198,9 @@ struct ctl_table_header *register_sysctl_paths(const struct ctl_path *path,
 void unregister_sysctl_table(struct ctl_table_header * table);
 
 extern int sysctl_init(void);
+extern void __register_sysctl_init(const char *path, struct ctl_table *table,
+				 const char *table_name);
+#define register_sysctl_init(path, table) __register_sysctl_init(path, table, #table)
 
 extern struct ctl_table sysctl_mount_point[];
 
Signature · 1 line
2.39.1
#2 lore raw

PATCH v2 2/15 panic: unset panic_on_warn inside panic()

In reply to Eric Biggers

From: Tiezhu Yang <[email protected]>

commit 1a2383e8b84c0451fd9b1eec3b9aab16f30b597c upstream.

In the current code, the following three places need to unset panic_on_warn before calling panic() to avoid recursive panics:

kernel/kcsan/report.c: print_report()
kernel/sched/core.c: __schedule_bug()
mm/kfence/report.c: kfence_report_error()

In order to avoid copy-pasting "panic_on_warn = 0" all over the places, it is better to move it inside panic() and then remove it from the other places.

---
 kernel/panic.c | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)
kernel/panic.c +11 -9
index 207ceac3a432e..bd7c3ea3bf1e6 100644
@@ -139,6 +139,16 @@ void panic(const char *fmt, ...)
 	int old_cpu, this_cpu;
 	bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers;
 
+	if (panic_on_warn) {
+		/*
+		 * This thread may hit another WARN() in the panic path.
+		 * Resetting this prevents additional WARN() from panicking the
+		 * system on this thread.  Other threads are blocked by the
+		 * panic_mutex in panic().
+		 */
+		panic_on_warn = 0;
+	}
+
 	/*
 	 * Disable local interrupts. This will prevent panic_smp_self_stop
 	 * from deadlocking the first cpu that invokes the panic, since
@@ -536,16 +546,8 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
 	if (args)
 		vprintk(args->fmt, args->args);
 
-	if (panic_on_warn) {
-		/*
-		 * This thread may hit another WARN() in the panic path.
-		 * Resetting this prevents additional WARN() from panicking the
-		 * system on this thread.  Other threads are blocked by the
-		 * panic_mutex in panic().
-		 */
-		panic_on_warn = 0;
+	if (panic_on_warn)
 		panic("panic_on_warn set ...\n");
-	}
 
 	print_modules();
 
Signature · 1 line
2.39.1
#3 lore raw

PATCH v2 3/15 exit: Add and use make_task_dead.

In reply to Eric Biggers

From: "Eric W. Biederman" <[email protected]>

commit 0e25498f8cd43c1b5aa327f373dd094e9a006da7 upstream.

There are two big uses of do_exit. The first is it's design use to be the guts of the exit(2) system call. The second use is to terminate a task after something catastrophic has happened like a NULL pointer in kernel code.

Add a function make_task_dead that is initialy exactly the same as do_exit to cover the cases where do_exit is called to handle catastrophic failure. In time this can probably be reduced to just a light wrapper around do_task_dead. For now keep it exactly the same so that there will be no behavioral differences introducing this new concept.

Replace all of the uses of do_exit that use it for catastraphic task cleanup with make_task_dead to make it clear what the code is doing.

As part of this rename rewind_stack_do_exit
rewind_stack_and_make_dead.

---
 arch/alpha/kernel/traps.c           | 6 +++---
 arch/alpha/mm/fault.c               | 2 +-
 arch/arm/kernel/traps.c             | 2 +-
 arch/arm/mm/fault.c                 | 2 +-
 arch/arm64/kernel/traps.c           | 2 +-
 arch/arm64/mm/fault.c               | 2 +-
 arch/h8300/kernel/traps.c           | 2 +-
 arch/h8300/mm/fault.c               | 2 +-
 arch/hexagon/kernel/traps.c         | 2 +-
 arch/ia64/kernel/mca_drv.c          | 2 +-
 arch/ia64/kernel/traps.c            | 2 +-
 arch/ia64/mm/fault.c                | 2 +-
 arch/m68k/kernel/traps.c            | 2 +-
 arch/m68k/mm/fault.c                | 2 +-
 arch/microblaze/kernel/exceptions.c | 4 ++--
 arch/mips/kernel/traps.c            | 2 +-
 arch/nios2/kernel/traps.c           | 4 ++--
 arch/openrisc/kernel/traps.c        | 2 +-
 arch/parisc/kernel/traps.c          | 2 +-
 arch/powerpc/kernel/traps.c         | 2 +-
 arch/s390/kernel/dumpstack.c        | 2 +-
 arch/s390/kernel/nmi.c              | 2 +-
 arch/sh/kernel/traps.c              | 2 +-
 arch/sparc/kernel/traps_32.c        | 4 +---
 arch/sparc/kernel/traps_64.c        | 4 +---
 arch/x86/entry/entry_32.S           | 6 +++---
 arch/x86/entry/entry_64.S           | 6 +++---
 arch/x86/kernel/dumpstack.c         | 4 ++--
 arch/xtensa/kernel/traps.c          | 2 +-
 include/linux/sched/task.h          | 1 +
 kernel/exit.c                       | 9 +++++++++
 tools/objtool/check.c               | 3 ++-
 32 files changed, 50 insertions(+), 43 deletions(-)
arch/alpha/kernel/traps.c +3 -3
index f43bd05dede26..6a45f392c6728 100644
@@ -192,7 +192,7 @@ die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15)
 		local_irq_enable();
 		while (1);
 	}
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 #ifndef CONFIG_MATHEMU
@@ -609,7 +609,7 @@ do_entUna(void * va, unsigned long opcode, unsigned long reg,
 
 	printk("Bad unaligned kernel access at %016lx: %p %lx %lu\n",
 		pc, va, opcode, reg);
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 
 got_exception:
 	/* Ok, we caught the exception, but we don't want it.  Is there
@@ -664,7 +664,7 @@ do_entUna(void * va, unsigned long opcode, unsigned long reg,
 		local_irq_enable();
 		while (1);
 	}
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 /*
arch/alpha/mm/fault.c +1 -1
index e9392302c5dab..a85c1b18d0bc9 100644
@@ -206,7 +206,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr,
 	printk(KERN_ALERT "Unable to handle kernel paging request at "
 	       "virtual address %016lx\n", address);
 	die_if_kernel("Oops", regs, cause, (unsigned long*)regs - 16);
-	do_exit(SIGKILL);
+	make_task_dead(SIGKILL);
 
 	/* We ran out of memory, or some other thing happened to us that
 	   made us unable to handle the page fault gracefully.  */
arch/arm/kernel/traps.c +1 -1
index 7d81d4a1f5a9c..df7a92c5df6c5 100644
@@ -342,7 +342,7 @@ static void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
 	if (panic_on_oops)
 		panic("Fatal exception");
 	if (signr)
-		do_exit(signr);
+		make_task_dead(signr);
 }
 
 /*
arch/arm/mm/fault.c +1 -1
index 9bb446cc135d1..45e73596b524c 100644
@@ -150,7 +150,7 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
 	show_pte(mm, addr);
 	die("Oops", regs, fsr);
 	bust_spinlocks(0);
-	do_exit(SIGKILL);
+	make_task_dead(SIGKILL);
 }
 
 /*
arch/arm64/kernel/traps.c +1 -1
index b30d23431fe11..ee19e6463e5a4 100644
@@ -223,7 +223,7 @@ void die(const char *str, struct pt_regs *regs, int err)
 	raw_spin_unlock_irqrestore(&die_lock, flags);
 
 	if (ret != NOTIFY_STOP)
-		do_exit(SIGSEGV);
+		make_task_dead(SIGSEGV);
 }
 
 void arm64_notify_die(const char *str, struct pt_regs *regs,
arch/arm64/mm/fault.c +1 -1
index 617787e4081f1..d191b046d4c18 100644
@@ -288,7 +288,7 @@ static void __do_kernel_fault(unsigned long addr, unsigned int esr,
 	show_pte(addr);
 	die("Oops", regs, esr);
 	bust_spinlocks(0);
-	do_exit(SIGKILL);
+	make_task_dead(SIGKILL);
 }
 
 /*
arch/h8300/kernel/traps.c +1 -1
index e47a9e0dc278f..a284c126f07a6 100644
@@ -110,7 +110,7 @@ void die(const char *str, struct pt_regs *fp, unsigned long err)
 	dump(fp);
 
 	spin_unlock_irq(&die_lock);
-	do_exit(SIGSEGV);
+	make_dead_task(SIGSEGV);
 }
 
 static int kstack_depth_to_print = 24;
arch/h8300/mm/fault.c +1 -1
index fabffb83930af..a8d8fc63780e4 100644
@@ -52,7 +52,7 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
 	printk(" at virtual address %08lx\n", address);
 	if (!user_mode(regs))
 		die("Oops", regs, error_code);
-	do_exit(SIGKILL);
+	make_dead_task(SIGKILL);
 
 	return 1;
 }
arch/hexagon/kernel/traps.c +1 -1
index 2942a9204a9aa..797608772f8a4 100644
@@ -234,7 +234,7 @@ int die(const char *str, struct pt_regs *regs, long err)
 		panic("Fatal exception");
 
 	oops_exit();
-	do_exit(err);
+	make_dead_task(err);
 	return 0;
 }
 
arch/ia64/kernel/mca_drv.c +1 -1
index 3503d488e9b3f..7c9d63ef1cb28 100644
@@ -176,7 +176,7 @@ mca_handler_bh(unsigned long paddr, void *iip, unsigned long ipsr)
 	spin_unlock(&mca_bh_lock);
 
 	/* This process is about to be killed itself */
-	do_exit(SIGKILL);
+	make_task_dead(SIGKILL);
 }
 
 /**
arch/ia64/kernel/traps.c +1 -1
index 6d4e76a4267f1..2bab65c10d236 100644
@@ -85,7 +85,7 @@ die (const char *str, struct pt_regs *regs, long err)
 	if (panic_on_oops)
 		panic("Fatal exception");
 
-  	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 	return 0;
 }
 
arch/ia64/mm/fault.c +1 -1
index dfdc152d6737b..23f23e051ee24 100644
@@ -300,7 +300,7 @@ ia64_do_page_fault (unsigned long address, unsigned long isr, struct pt_regs *re
 		regs = NULL;
 	bust_spinlocks(0);
 	if (regs)
-		do_exit(SIGKILL);
+		make_task_dead(SIGKILL);
 	return;
 
   out_of_memory:
arch/m68k/kernel/traps.c +1 -1
index c1cc4e99aa945..5c72deb117a8e 100644
@@ -1141,7 +1141,7 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr)
 	pr_crit("%s: %08x\n", str, nr);
 	show_registers(fp);
 	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 asmlinkage void set_esp0(unsigned long ssp)
arch/m68k/mm/fault.c +1 -1
index 127d7c1f2090c..4ef6057592f12 100644
@@ -50,7 +50,7 @@ int send_fault_sig(struct pt_regs *regs)
 			pr_alert("Unable to handle kernel access");
 		pr_cont(" at virtual address %p\n", siginfo.si_addr);
 		die_if_kernel("Oops", regs, 0 /*error_code*/);
-		do_exit(SIGKILL);
+		make_task_dead(SIGKILL);
 	}
 
 	return 1;
arch/microblaze/kernel/exceptions.c +2 -2
index e6f338d0496bb..3066d7fe484bd 100644
@@ -44,10 +44,10 @@ void die(const char *str, struct pt_regs *fp, long err)
 	pr_warn("Oops: %s, sig: %ld\n", str, err);
 	show_regs(fp);
 	spin_unlock_irq(&die_lock);
-	/* do_exit() should take care of panic'ing from an interrupt
+	/* make_task_dead() should take care of panic'ing from an interrupt
 	 * context so we don't handle it here
 	 */
-	do_exit(err);
+	make_task_dead(err);
 }
 
 /* for user application debugging */
arch/mips/kernel/traps.c +1 -1
index abbc64788008a..a8f166ff2762b 100644
@@ -414,7 +414,7 @@ void __noreturn die(const char *str, struct pt_regs *regs)
 	if (regs && kexec_should_crash(current))
 		crash_kexec(regs);
 
-	do_exit(sig);
+	make_task_dead(sig);
 }
 
 extern struct exception_table_entry __start___dbe_table[];
arch/nios2/kernel/traps.c +2 -2
index 8184e7d6b3857..5dadd36d13ce3 100644
@@ -43,10 +43,10 @@ void die(const char *str, struct pt_regs *regs, long err)
 	show_regs(regs);
 	spin_unlock_irq(&die_lock);
 	/*
-	 * do_exit() should take care of panic'ing from an interrupt
+	 * make_task_dead() should take care of panic'ing from an interrupt
 	 * context so we don't handle it here
 	 */
-	do_exit(err);
+	make_task_dead(err);
 }
 
 void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr)
arch/openrisc/kernel/traps.c +1 -1
index 0d44e8007ad6e..a8fb061f1c888 100644
@@ -265,7 +265,7 @@ void die(const char *str, struct pt_regs *regs, long err)
 	__asm__ __volatile__("l.nop   1");
 	do {} while (1);
 #endif
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 /* This is normally the 'Oops' routine */
arch/parisc/kernel/traps.c +1 -1
index 346456c43aa0b..36582e23f9b9d 100644
@@ -290,7 +290,7 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err)
 		panic("Fatal exception");
 
 	oops_exit();
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 /* gdb uses break 4,8 */
arch/powerpc/kernel/traps.c +1 -1
index 05c1aabad01c6..a507a6874d420 100644
@@ -211,7 +211,7 @@ static void oops_end(unsigned long flags, struct pt_regs *regs,
 		panic("Fatal exception in interrupt");
 	if (panic_on_oops)
 		panic("Fatal exception");
-	do_exit(signr);
+	make_task_dead(signr);
 }
 NOKPROBE_SYMBOL(oops_end);
 
arch/s390/kernel/dumpstack.c +1 -1
index 2aa545dca4d53..d9513a9f0db12 100644
@@ -186,5 +186,5 @@ void die(struct pt_regs *regs, const char *str)
 	if (panic_on_oops)
 		panic("Fatal exception: panic_on_oops");
 	oops_exit();
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
arch/s390/kernel/nmi.c +1 -1
index 31d03a84126c5..5dca748d3815c 100644
@@ -94,7 +94,7 @@ void s390_handle_mcck(void)
 		       "malfunction (code 0x%016lx).\n", mcck.mcck_code);
 		printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
 		       current->comm, current->pid);
-		do_exit(SIGSEGV);
+		make_task_dead(SIGSEGV);
 	}
 }
 EXPORT_SYMBOL_GPL(s390_handle_mcck);
arch/sh/kernel/traps.c +1 -1
index 8b49cced663dc..5fafbef7849b1 100644
@@ -57,7 +57,7 @@ void die(const char *str, struct pt_regs *regs, long err)
 	if (panic_on_oops)
 		panic("Fatal exception");
 
-	do_exit(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 void die_if_kernel(const char *str, struct pt_regs *regs, long err)
arch/sparc/kernel/traps_32.c +1 -3
index b1ed763e47877..fb0576f3b1de0 100644
@@ -86,9 +86,7 @@ void __noreturn die_if_kernel(char *str, struct pt_regs *regs)
 	}
 	printk("Instruction DUMP:");
 	instruction_dump ((unsigned long *) regs->pc);
-	if(regs->psr & PSR_PS)
-		do_exit(SIGKILL);
-	do_exit(SIGSEGV);
+	make_task_dead((regs->psr & PSR_PS) ? SIGKILL : SIGSEGV);
 }
 
 void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
arch/sparc/kernel/traps_64.c +1 -3
index 6ab9b87dbca8c..cfc06eeeb4f35 100644
@@ -2547,9 +2547,7 @@ void __noreturn die_if_kernel(char *str, struct pt_regs *regs)
 	}
 	if (panic_on_oops)
 		panic("Fatal exception");
-	if (regs->tstate & TSTATE_PRIV)
-		do_exit(SIGKILL);
-	do_exit(SIGSEGV);
+	make_task_dead((regs->tstate & TSTATE_PRIV)? SIGKILL : SIGSEGV);
 }
 EXPORT_SYMBOL(die_if_kernel);
 
arch/x86/entry/entry_32.S +3 -3
index dbcea4281c309..1fdedb2eaef35 100644
@@ -1068,13 +1068,13 @@ ENTRY(async_page_fault)
 END(async_page_fault)
 #endif
 
-ENTRY(rewind_stack_do_exit)
+ENTRY(rewind_stack_and_make_dead)
 	/* Prevent any naive code from trying to unwind to our caller. */
 	xorl	%ebp, %ebp
 
 	movl	PER_CPU_VAR(cpu_current_top_of_stack), %esi
 	leal	-TOP_OF_KERNEL_STACK_PADDING-PTREGS_SIZE(%esi), %esp
 
-	call	do_exit
+	call	make_task_dead
 1:	jmp 1b
-END(rewind_stack_do_exit)
+END(rewind_stack_and_make_dead)
arch/x86/entry/entry_64.S +3 -3
index 637a23d404e95..b57f15b51ed52 100644
@@ -1672,7 +1672,7 @@ ENTRY(ignore_sysret)
 	sysret
 END(ignore_sysret)
 
-ENTRY(rewind_stack_do_exit)
+ENTRY(rewind_stack_and_make_dead)
 	UNWIND_HINT_FUNC
 	/* Prevent any naive code from trying to unwind to our caller. */
 	xorl	%ebp, %ebp
@@ -1681,5 +1681,5 @@ ENTRY(rewind_stack_do_exit)
 	leaq	-PTREGS_SIZE(%rax), %rsp
 	UNWIND_HINT_REGS
 
-	call	do_exit
-END(rewind_stack_do_exit)
+	call	make_task_dead
+END(rewind_stack_and_make_dead)
arch/x86/kernel/dumpstack.c +2 -2
index 224de37821e4e..92585a755410b 100644
@@ -271,7 +271,7 @@ unsigned long oops_begin(void)
 EXPORT_SYMBOL_GPL(oops_begin);
 NOKPROBE_SYMBOL(oops_begin);
 
-void __noreturn rewind_stack_do_exit(int signr);
+void __noreturn rewind_stack_and_make_dead(int signr);
 
 void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
 {
@@ -303,7 +303,7 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr)
 	 * reuse the task stack and that existing poisons are invalid.
 	 */
 	kasan_unpoison_task_stack(current);
-	rewind_stack_do_exit(signr);
+	rewind_stack_and_make_dead(signr);
 }
 NOKPROBE_SYMBOL(oops_end);
 
arch/xtensa/kernel/traps.c +1 -1
index 2986bc88a18e7..09ba7436d1bd0 100644
@@ -547,5 +547,5 @@ void die(const char * str, struct pt_regs * regs, long err)
 	if (panic_on_oops)
 		panic("Fatal exception");
 
-	do_exit(err);
+	make_task_dead(err);
 }
include/linux/sched/task.h +1 -0
index c3d157a370734..3ef0986cf0ea3 100644
@@ -36,6 +36,7 @@ extern int sched_fork(unsigned long clone_flags, struct task_struct *p);
 extern void sched_dead(struct task_struct *p);
 
 void __noreturn do_task_dead(void);
+void __noreturn make_task_dead(int signr);
 
 extern void proc_caches_init(void);
 
kernel/exit.c +9 -0
index 9e70577b818ab..1e778b88fa3fe 100644
@@ -920,6 +920,15 @@ void __noreturn do_exit(long code)
 }
 EXPORT_SYMBOL_GPL(do_exit);
 
+void __noreturn make_task_dead(int signr)
+{
+	/*
+	 * Take the task off the cpu after something catastrophic has
+	 * happened.
+	 */
+	do_exit(signr);
+}
+
 void complete_and_exit(struct completion *comp, long code)
 {
 	if (comp)
tools/objtool/check.c +2 -1
index 2c8e2dae17016..9048b02b54474 100644
@@ -159,6 +159,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
 		"panic",
 		"do_exit",
 		"do_task_dead",
+		"make_task_dead",
 		"__module_put_and_exit",
 		"complete_and_exit",
 		"kvm_spurious_fault",
@@ -166,7 +167,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
 		"lbug_with_loc",
 		"fortify_panic",
 		"machine_real_restart",
-		"rewind_stack_do_exit",
+		"rewind_stack_and_make_dead"
 	};
 
 	if (func->bind == STB_WEAK)
Signature · 1 line
2.39.1
#4 lore raw

PATCH v2 4/15 objtool: Add a missing comma to avoid string concatenation

In reply to Eric Biggers

From: "Eric W. Biederman" <[email protected]>

commit 1fb466dff904e4a72282af336f2c355f011eec61 upstream.

Recently the kbuild robot reported two new errors:

Quoted 2 lines
Quoted 2 lines

lib/kunit/kunit-example-test.o: warning: objtool: .text.unlikely: unexpected end of section arch/x86/kernel/dumpstack.o: warning: objtool: oops_end() falls through to next function show_opcodes()

I don't know why they did not occur in my test setup but after digging it I realized I had accidentally dropped a comma in tools/objtool/check.c when I renamed rewind_stack_do_exit to rewind_stack_and_make_dead.

Add that comma back to fix objtool errors.

---
 tools/objtool/check.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
tools/objtool/check.c +1 -1
index 9048b02b54474..e93c061654a79 100644
@@ -167,7 +167,7 @@ static int __dead_end_function(struct objtool_file *file, struct symbol *func,
 		"lbug_with_loc",
 		"fortify_panic",
 		"machine_real_restart",
-		"rewind_stack_and_make_dead"
+		"rewind_stack_and_make_dead",
 	};
 
 	if (func->bind == STB_WEAK)
Signature · 1 line
2.39.1
#5 lore raw

PATCH v2 5/15 hexagon: Fix function name in die()

In reply to Eric Biggers

From: Nathan Chancellor <[email protected]>

commit 4f0712ccec09c071e221242a2db9a6779a55a949 upstream.

When building ARCH=hexagon defconfig:

arch/hexagon/kernel/traps.c:217:2: error: implicit declaration of function 'make_dead_task' [-Werror,-Wimplicit-function-declaration]

        make_dead_task(err);
        ^

The function's name is make_task_dead(), change it so there is no more build error.

---
 arch/hexagon/kernel/traps.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/hexagon/kernel/traps.c +1 -1
index 797608772f8a4..65330343bdc33 100644
@@ -234,7 +234,7 @@ int die(const char *str, struct pt_regs *regs, long err)
 		panic("Fatal exception");
 
 	oops_exit();
-	make_dead_task(err);
+	make_task_dead(err);
 	return 0;
 }
 
Signature · 1 line
2.39.1
#6 lore raw

PATCH v2 6/15 h8300: Fix build errors from do_exit() to make_task_dead() transition

In reply to Eric Biggers

From: Nathan Chancellor <[email protected]>

commit ab4ababdf77ccc56c7301c751dff49c79709c51c upstream.

When building ARCH=h8300 defconfig:

arch/h8300/kernel/traps.c: In function 'die':
arch/h8300/kernel/traps.c:109:2: error: implicit declaration of function 'make_dead_task' [-Werror=implicit-function-declaration]

  109 |  make_dead_task(SIGSEGV);
      |  ^~~~~~~~~~~~~~

arch/h8300/mm/fault.c: In function 'do_page_fault':
arch/h8300/mm/fault.c:54:2: error: implicit declaration of function 'make_dead_task' [-Werror=implicit-function-declaration]

   54 |  make_dead_task(SIGKILL);
      |  ^~~~~~~~~~~~~~

The function's name is make_task_dead(), change it so there is no more build error.

Additionally, include linux/sched/task.h in arch/h8300/kernel/traps.c to avoid the same error because do_exit()'s declaration is in kernel.h but make_task_dead()'s is in task.h, which is not included in traps.c.

---
 arch/h8300/kernel/traps.c | 3 ++-
 arch/h8300/mm/fault.c     | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)
arch/h8300/kernel/traps.c +2 -1
index a284c126f07a6..090adaee4b84c 100644
@@ -17,6 +17,7 @@
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/sched/debug.h>
+#include <linux/sched/task.h>
 #include <linux/mm_types.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -110,7 +111,7 @@ void die(const char *str, struct pt_regs *fp, unsigned long err)
 	dump(fp);
 
 	spin_unlock_irq(&die_lock);
-	make_dead_task(SIGSEGV);
+	make_task_dead(SIGSEGV);
 }
 
 static int kstack_depth_to_print = 24;
arch/h8300/mm/fault.c +1 -1
index a8d8fc63780e4..573825c3cb708 100644
@@ -52,7 +52,7 @@ asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
 	printk(" at virtual address %08lx\n", address);
 	if (!user_mode(regs))
 		die("Oops", regs, error_code);
-	make_dead_task(SIGKILL);
+	make_task_dead(SIGKILL);
 
 	return 1;
 }
Signature · 1 line
2.39.1
#7 lore raw

PATCH v2 7/15 ia64: make IA64_MCA_RECOVERY bool instead of tristate

In reply to Eric Biggers

From: Randy Dunlap <[email protected]>

commit dbecf9b8b8ce580f4e11afed9d61e8aa294cddd2 upstream.

In linux-next, IA64_MCA_RECOVERY uses the (new) function make_task_dead(), which is not exported for use by modules. Instead of exporting it for one user, convert IA64_MCA_RECOVERY to be a bool Kconfig symbol.

In a config file from "kernel test robot <[email protected]>" for a different problem, this linker error was exposed when CONFIG_IA64_MCA_RECOVERY=m.

Fixes this build error:

ERROR: modpost: "make_task_dead" [arch/ia64/kernel/mca_recovery.ko] undefined!

---
 arch/ia64/Kconfig | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
arch/ia64/Kconfig +1 -1
index 1efc444f5fa1a..f8dac6bd17dd2 100644
@@ -461,7 +461,7 @@ config ARCH_PROC_KCORE_TEXT
 	depends on PROC_KCORE
 
 config IA64_MCA_RECOVERY
-	tristate "MCA recovery from errors other than TLB."
+	bool "MCA recovery from errors other than TLB."
 
 config PERFMON
 	bool "Performance monitor support"
Signature · 1 line
2.39.1
#8 lore raw

PATCH v2 8/15 exit: Put an upper limit on how often we can oops

In reply to Eric Biggers

From: Jann Horn <[email protected]>

commit d4ccd54d28d3c8598e2354acc13e28c060961dbb upstream.

Many Linux systems are configured to not panic on oops; but allowing an attacker to oops the system **really** often can make even bugs that look completely unexploitable exploitable (like NULL dereferences and such) if each crash elevates a refcount by one or a lock is taken in read mode, and this causes a counter to eventually overflow.

The most interesting counters for this are 32 bits wide (like open-coded refcounts that don't use refcount_t). (The ldsem reader count on 32-bit platforms is just 16 bits, but probably nobody cares about 32-bit platforms that much nowadays.)

So let's panic the system if the kernel is constantly oopsing.

The speed of oopsing 2^32 times probably depends on several factors, like how long the stack trace is and which unwinder you're using; an empirically important one is whether your console is showing a graphical environment or a text console that oopses will be printed to.
In a quick single-threaded benchmark, it looks like oopsing in a vfork() child with a very short stack trace only takes ~510 microseconds per run when a graphical console is active; but switching to a text console that oopses are printed to slows it down around 87x, to ~45 milliseconds per run.
(Adding more threads makes this faster, but the actual oops printing happens under &die_lock on x86, so you can maybe speed this up by a factor of around 2 and then any further improvement gets eaten up by lock contention.)

It looks like it would take around 8-12 days to overflow a 32-bit counter with repeated oopsing on a multi-core X86 system running a graphical environment; both me (in an X86 VM) and Seth (with a distro kernel on normal hardware in a standard configuration) got numbers in that ballpark.

12 days aren't *that* short on a desktop system, and you'd likely need much longer on a typical server system (assuming that people don't run graphical desktop environments on their servers), and this is a *very* noisy and violent approach to exploiting the kernel; and it also seems to take orders of magnitude longer on some machines, probably because stuff like EFI pstore will slow it down a ton if that's active.

---
 Documentation/sysctl/kernel.txt |  9 +++++++
 kernel/exit.c                   | 43 +++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)
Documentation/sysctl/kernel.txt +9 -0
index 37b612a17c461..7b04c616c5901 100644
@@ -48,6 +48,7 @@ show up in /proc/sys/kernel:
 - msgmnb
 - msgmni
 - nmi_watchdog
+- oops_limit
 - osrelease
 - ostype
 - overflowgid
@@ -515,6 +516,14 @@ scanned for a given scan.
 
 ==============================================================
 
+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:
 
 # cat osrelease
kernel/exit.c +43 -0

59 lines not on this page · raw message

Signature · 1 line
2.39.1
#9 lore raw

PATCH v2 9/15 exit: Expose "oops_count" to sysfs

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 9db89b41117024f80b38b15954017fb293133364 upstream.

Since Oops count is now tracked and is a fairly interesting signal, add the entry /sys/kernel/oops_count to expose it to userspace.

---
 .../ABI/testing/sysfs-kernel-oops_count       |  6 +++++
 kernel/exit.c                                 | 22 +++++++++++++++++--
 2 files changed, 26 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-oops_count
Documentation/ABI/testing/sysfs-kernel-oops_count new file +6 -0
new file mode 100644
index 0000000000000..156cca9dbc960
@@ -0,0 +1,6 @@
+What:		/sys/kernel/oops_count
+Date:		November 2022
+KernelVersion:	6.2.0
+Contact:	Linux Kernel Hardening List <[email protected]>
+Description:
+		Shows how many times the system has Oopsed since last boot.
kernel/exit.c +20 -2
index 482bcaf158127..138b110bf83a1 100644
@@ -62,6 +62,7 @@
 #include <linux/random.h>
 #include <linux/rcuwait.h>
 #include <linux/compat.h>
+#include <linux/sysfs.h>
 
 #include <linux/uaccess.h>
 #include <asm/unistd.h>
@@ -95,6 +96,25 @@ static __init int kernel_exit_sysctls_init(void)
 late_initcall(kernel_exit_sysctls_init);
 #endif
 
+static atomic_t oops_count = ATOMIC_INIT(0);
+
+#ifdef CONFIG_SYSFS
+static ssize_t oops_count_show(struct kobject *kobj, struct kobj_attribute *attr,
+			       char *page)
+{
+	return sysfs_emit(page, "%d\n", atomic_read(&oops_count));
+}
+
+static struct kobj_attribute oops_count_attr = __ATTR_RO(oops_count);
+
+static __init int kernel_exit_sysfs_init(void)
+{
+	sysfs_add_file_to_group(kernel_kobj, &oops_count_attr.attr, NULL);
+	return 0;
+}
+late_initcall(kernel_exit_sysfs_init);
+#endif
+
 static void __unhash_process(struct task_struct *p, bool group_dead)
 {
 	nr_threads--;
@@ -949,8 +969,6 @@ EXPORT_SYMBOL_GPL(do_exit);
 
 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.
Signature · 1 line
2.39.1
#10 lore raw

PATCH v2 10/15 exit: Allow oops_limit to be disabled

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit de92f65719cd672f4b48397540b9f9eff67eca40 upstream.

In preparation for keeping oops_limit logic in sync with warn_limit, have oops_limit == 0 disable checking the Oops counter.

Cc Jann Horn <[email protected]>
Cc Jonathan Corbet <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Baolin Wang <[email protected]>
Cc "Jason A. Donenfeld" <[email protected]>
Cc Eric Biggers <[email protected]>
Cc Huang Ying <[email protected]>
Cc "Eric W. Biederman" <[email protected]>
Cc Arnd Bergmann <[email protected]>
---
 Documentation/sysctl/kernel.txt | 5 +++--
 kernel/exit.c                   | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)
Documentation/sysctl/kernel.txt +3 -2
index 7b04c616c5901..b6124a4475fb7 100644
@@ -519,8 +519,9 @@ scanned for a given scan.
 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``.
+``panic_on_oops`` is not set. Setting this to 0 disables checking
+the count. Setting this to  1 has the same effect as setting
+``panic_on_oops=1``. The default value is 10000.
 
 ==============================================================
 
kernel/exit.c +1 -1
index 138b110bf83a1..73103e008a627 100644
@@ -984,7 +984,7 @@ void __noreturn make_task_dead(int signr)
 	 * 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))
+	if (atomic_inc_return(&oops_count) >= READ_ONCE(oops_limit) && oops_limit)
 		panic("Oopsed too often (kernel.oops_limit is %d)", oops_limit);
 
 	do_exit(signr);
Signature · 1 line
2.39.1
#11 lore raw

PATCH v2 11/15 panic: Consolidate open-coded panic_on_warn checks

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 79cc1ba7badf9e7a12af99695a557e9ce27ee967 upstream.

Several run-time checkers (KASAN, UBSAN, KFENCE, KCSAN, sched) roll their own warnings, and each check "panic_on_warn". Consolidate this into a single function so that future instrumentation can be added in a single location.

Cc Marco Elver <[email protected]>
Cc Dmitry Vyukov <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Juri Lelli <[email protected]>
Cc Vincent Guittot <[email protected]>
Cc Dietmar Eggemann <[email protected]>
Cc Steven Rostedt <[email protected]>
Cc Ben Segall <[email protected]>
Cc Mel Gorman <[email protected]>
Cc Daniel Bristot de Oliveira <[email protected]>
Cc Valentin Schneider <[email protected]>
Cc Andrey Ryabinin <[email protected]>
Cc Alexander Potapenko <[email protected]>
Cc Andrey Konovalov <[email protected]>
Cc Vincenzo Frascino <[email protected]>
Cc Andrew Morton <[email protected]>
Cc David Gow <[email protected]>
Cc tangmeng <[email protected]>
Cc Jann Horn <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Petr Mladek <[email protected]>
Cc "Paul E. McKenney" <[email protected]>
Cc Sebastian Andrzej Siewior <[email protected]>
Cc "Guilherme G. Piccoli" <[email protected]>
Cc Tiezhu Yang <[email protected]>
---
 include/linux/kernel.h | 1 +
 kernel/panic.c         | 9 +++++++--
 kernel/sched/core.c    | 3 +--
 mm/kasan/report.c      | 3 +--
 4 files changed, 10 insertions(+), 6 deletions(-)
include/linux/kernel.h +1 -0
index 22b9146655958..a4ac278d02d0a 100644
@@ -293,6 +293,7 @@ extern long (*panic_blink)(int state);
 __printf(1, 2)
 void panic(const char *fmt, ...) __noreturn __cold;
 void nmi_panic(struct pt_regs *regs, const char *msg);
+void check_panic_on_warn(const char *origin);
 extern void oops_enter(void);
 extern void oops_exit(void);
 void print_oops_end_marker(void);
kernel/panic.c +7 -2
index bd7c3ea3bf1e6..8e3460e985904 100644
@@ -122,6 +122,12 @@ void nmi_panic(struct pt_regs *regs, const char *msg)
 }
 EXPORT_SYMBOL(nmi_panic);
 
+void check_panic_on_warn(const char *origin)
+{
+	if (panic_on_warn)
+		panic("%s: panic_on_warn set ...\n", origin);
+}
+
 /**
  *	panic - halt the system
  *	@fmt: The text string to print
@@ -546,8 +552,7 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
 	if (args)
 		vprintk(args->fmt, args->args);
 
-	if (panic_on_warn)
-		panic("panic_on_warn set ...\n");
+	check_panic_on_warn("kernel");
 
 	print_modules();
 
kernel/sched/core.c +1 -2
index 544a1cb66d90d..5dc66377864a9 100644
@@ -3185,8 +3185,7 @@ static noinline void __schedule_bug(struct task_struct *prev)
 		print_ip_sym(preempt_disable_ip);
 		pr_cont("\n");
 	}
-	if (panic_on_warn)
-		panic("scheduling while atomic\n");
+	check_panic_on_warn("scheduling while atomic");
 
 	dump_stack();
 	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
mm/kasan/report.c +1 -2
index 6bcfb01ba0386..1c96c83f97751 100644
@@ -172,8 +172,7 @@ static void kasan_end_report(unsigned long *flags)
 	pr_err("==================================================================\n");
 	add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
 	spin_unlock_irqrestore(&report_lock, *flags);
-	if (panic_on_warn)
-		panic("panic_on_warn set ...\n");
+	check_panic_on_warn("KASAN");
 	kasan_enable_current();
 }
 
Signature · 1 line
2.39.1
maintainer Applied
#12 lore raw

Patch "panic: Consolidate open-coded panic_on_warn checks" has been added to the 4.14-stable tree

This is a note to let you know that I've just added the patch titled

panic: Consolidate open-coded panic_on_warn checks

to the 4.14-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
panic-consolidate-open-coded-panic_on_warn-checks.patch
and it can be found in the queue-4.14 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.

From [email protected] Fri Feb 3 01:35:49 2023
From: Eric Biggers <[email protected]>
Date: Thu, 2 Feb 2023 16:33:50 -0800
Subject: panic: Consolidate open-coded panic_on_warn checks
To: [email protected]
Cc: Harshit Mogalapalli <[email protected]>, Kees Cook <[email protected]>, SeongJae Park <[email protected]>, Seth Jenkins <[email protected]>, Jann Horn <[email protected]>, "Eric W . Biederman" <[email protected]>, [email protected], [email protected], Marco Elver <[email protected]>, Dmitry Vyukov <[email protected]>, Ingo Molnar <[email protected]>, Peter Zijlstra <[email protected]>, Juri Lelli <[email protected]>, Vincent Guittot <[email protected]>, Dietmar Eggemann <[email protected]>, Steven Rostedt <[email protected]>, Ben Segall <[email protected]>, Mel Gorman <[email protected]>, Daniel Bristot de Oliveira <[email protected]>, Valentin Schneider <[email protected]>, Andrey Ryabinin <[email protected]>, Alexander Potapenko <[email protected]>, Andrey Konovalov <[email protected]>, Vincenzo Frascino <[email protected]>, Andrew Morton <[email protected]>, David Gow <dav
idgow@go
ogle.com>, tangmeng <[email protected]>, Shuah Khan <[email protected]>, Petr Mladek <[email protected]>, "Paul E. McKenney" <[email protected]>, Sebastian Andrzej Siewior <[email protected]>, "Guilherme G. Piccoli" <[email protected]>, Tiezhu Yang <[email protected]>, [email protected], [email protected], Luis Chamberlain <[email protected]>
Message-ID: <[email protected]>

From: Kees Cook <[email protected]>

commit 79cc1ba7badf9e7a12af99695a557e9ce27ee967 upstream.

Several run-time checkers (KASAN, UBSAN, KFENCE, KCSAN, sched) roll their own warnings, and each check "panic_on_warn". Consolidate this into a single function so that future instrumentation can be added in a single location.

Cc Marco Elver <[email protected]>
Cc Dmitry Vyukov <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Juri Lelli <[email protected]>
Cc Vincent Guittot <[email protected]>
Cc Dietmar Eggemann <[email protected]>
Cc Steven Rostedt <[email protected]>
Cc Ben Segall <[email protected]>
Cc Mel Gorman <[email protected]>
Cc Daniel Bristot de Oliveira <[email protected]>
Cc Valentin Schneider <[email protected]>
Cc Andrey Ryabinin <[email protected]>
Cc Alexander Potapenko <[email protected]>
Cc Andrey Konovalov <[email protected]>
Cc Vincenzo Frascino <[email protected]>
Cc Andrew Morton <[email protected]>
Cc David Gow <[email protected]>
Cc tangmeng <[email protected]>
Cc Jann Horn <[email protected]>
Cc Shuah Khan <[email protected]>
Cc Petr Mladek <[email protected]>
Cc "Paul E. McKenney" <[email protected]>
Cc Sebastian Andrzej Siewior <[email protected]>
Cc "Guilherme G. Piccoli" <[email protected]>
Cc Tiezhu Yang <[email protected]>
---
 include/linux/kernel.h |    1 +
 kernel/panic.c         |    9 +++++++--
 kernel/sched/core.c    |    3 +--
 mm/kasan/report.c      |    3 +--
 4 files changed, 10 insertions(+), 6 deletions(-)
include/linux/kernel.h +1 -0
@@ -293,6 +293,7 @@ extern long (*panic_blink)(int state);
 __printf(1, 2)
 void panic(const char *fmt, ...) __noreturn __cold;
 void nmi_panic(struct pt_regs *regs, const char *msg);
+void check_panic_on_warn(const char *origin);
 extern void oops_enter(void);
 extern void oops_exit(void);
 void print_oops_end_marker(void);
kernel/panic.c +7 -2
@@ -122,6 +122,12 @@ void nmi_panic(struct pt_regs *regs, con
 }
 EXPORT_SYMBOL(nmi_panic);
 
+void check_panic_on_warn(const char *origin)
+{
+	if (panic_on_warn)
+		panic("%s: panic_on_warn set ...\n", origin);
+}
+
 /**
  *	panic - halt the system
  *	@fmt: The text string to print
@@ -546,8 +552,7 @@ void __warn(const char *file, int line,
 	if (args)
 		vprintk(args->fmt, args->args);
 
-	if (panic_on_warn)
-		panic("panic_on_warn set ...\n");
+	check_panic_on_warn("kernel");
 
 	print_modules();
 
kernel/sched/core.c +1 -2
@@ -3185,8 +3185,7 @@ static noinline void __schedule_bug(stru
 		print_ip_sym(preempt_disable_ip);
 		pr_cont("\n");
 	}
-	if (panic_on_warn)
-		panic("scheduling while atomic\n");
+	check_panic_on_warn("scheduling while atomic");
 
 	dump_stack();
 	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
mm/kasan/report.c +1 -2
@@ -172,8 +172,7 @@ static void kasan_end_report(unsigned lo
 	pr_err("==================================================================\n");
 	add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
 	spin_unlock_irqrestore(&report_lock, *flags);
-	if (panic_on_warn)
-		panic("panic_on_warn set ...\n");
+	check_panic_on_warn("KASAN");
 	kasan_enable_current();
 }
 

Patches currently in stable-queue which might be from [email protected] are

queue-4.14/panic-unset-panic_on_warn-inside-panic.patch queue-4.14/objtool-add-a-missing-comma-to-avoid-string-concatenation.patch queue-4.14/hexagon-fix-function-name-in-die.patch queue-4.14/exit-add-and-use-make_task_dead.patch queue-4.14/h8300-fix-build-errors-from-do_exit-to-make_task_dead-transition.patch queue-4.14/panic-consolidate-open-coded-panic_on_warn-checks.patch queue-4.14/exit-put-an-upper-limit-on-how-often-we-can-oops.patch queue-4.14/panic-introduce-warn_limit.patch queue-4.14/exit-allow-oops_limit-to-be-disabled.patch queue-4.14/ia64-make-ia64_mca_recovery-bool-instead-of-tristate.patch queue-4.14/exit-use-read_once-for-all-oops-warn-limit-reads.patch queue-4.14/exit-expose-oops_count-to-sysfs.patch queue-4.14/panic-expose-warn_count-to-sysfs.patch queue-4.14/docs-fix-path-paste-o-for-sys-kernel-warn_count.patch queue-4.14/sysctl-add-a-new-register_sysctl_init-interface.patch

#13 lore raw

PATCH v2 12/15 panic: Introduce warn_limit

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 9fc9e278a5c0b708eeffaf47d6eb0c82aa74ed78 upstream.

Like oops_limit, add warn_limit for limiting the number of warnings when panic_on_warn is not set.

Cc Jonathan Corbet <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Baolin Wang <[email protected]>
Cc "Jason A. Donenfeld" <[email protected]>
Cc Eric Biggers <[email protected]>
Cc Huang Ying <[email protected]>
Cc Petr Mladek <[email protected]>
Cc tangmeng <[email protected]>
Cc "Guilherme G. Piccoli" <[email protected]>
Cc Tiezhu Yang <[email protected]>
Cc Sebastian Andrzej Siewior <[email protected]>
---
 Documentation/sysctl/kernel.txt | 10 ++++++++++
 kernel/panic.c                  | 27 +++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)
Documentation/sysctl/kernel.txt +10 -0
index b6124a4475fb7..fefffc8e6ac5c 100644
@@ -94,6 +94,7 @@ show up in /proc/sys/kernel:
 - threads-max
 - unprivileged_bpf_disabled
 - unknown_nmi_panic
+- warn_limit
 - watchdog
 - watchdog_thresh
 - version
@@ -1072,6 +1073,15 @@ example.  If a system hangs up, try pressing the NMI switch.
 
 ==============================================================
 
+warn_limit:
+
+Number of kernel warnings after which the kernel should panic when
+``panic_on_warn`` is not set. Setting this to 0 disables checking
+the warning count. Setting this to 1 has the same effect as setting
+``panic_on_warn=1``. The default value is 0.
+
+==============================================================
+
 watchdog:
 
 This parameter can be used to disable or enable the soft lockup detector
kernel/panic.c +27 -0
index 8e3460e985904..bfe6e4c5cd0e7 100644
@@ -39,6 +39,7 @@ static int pause_on_oops_flag;
 static DEFINE_SPINLOCK(pause_on_oops_lock);
 bool crash_kexec_post_notifiers;
 int panic_on_warn __read_mostly;
+static unsigned int warn_limit __read_mostly;
 
 int panic_timeout = CONFIG_PANIC_TIMEOUT;
 EXPORT_SYMBOL_GPL(panic_timeout);
@@ -47,6 +48,26 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
 
 EXPORT_SYMBOL(panic_notifier_list);
 
+#ifdef CONFIG_SYSCTL
+static struct ctl_table kern_panic_table[] = {
+	{
+		.procname       = "warn_limit",
+		.data           = &warn_limit,
+		.maxlen         = sizeof(warn_limit),
+		.mode           = 0644,
+		.proc_handler   = proc_douintvec,
+	},
+	{ }
+};
+
+static __init int kernel_panic_sysctls_init(void)
+{
+	register_sysctl_init("kernel", kern_panic_table);
+	return 0;
+}
+late_initcall(kernel_panic_sysctls_init);
+#endif
+
 static long no_blink(int state)
 {
 	return 0;
@@ -124,8 +145,14 @@ EXPORT_SYMBOL(nmi_panic);
 
 void check_panic_on_warn(const char *origin)
 {
+	static atomic_t warn_count = ATOMIC_INIT(0);
+
 	if (panic_on_warn)
 		panic("%s: panic_on_warn set ...\n", origin);
+
+	if (atomic_inc_return(&warn_count) >= READ_ONCE(warn_limit) && warn_limit)
+		panic("%s: system warned too often (kernel.warn_limit is %d)",
+		      origin, warn_limit);
 }
 
 /**
Signature · 1 line
2.39.1
#14 lore raw

PATCH v2 13/15 panic: Expose "warn_count" to sysfs

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 8b05aa26336113c4cea25f1c333ee8cd4fc212a6 upstream.

Since Warn count is now tracked and is a fairly interesting signal, add the entry /sys/kernel/warn_count to expose it to userspace.

---
 .../ABI/testing/sysfs-kernel-warn_count       |  6 +++++
 kernel/panic.c                                | 22 +++++++++++++++++--
 2 files changed, 26 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-kernel-warn_count
Documentation/ABI/testing/sysfs-kernel-warn_count new file +6 -0
new file mode 100644
index 0000000000000..08f083d2fd51b
@@ -0,0 +1,6 @@
+What:		/sys/kernel/oops_count
+Date:		November 2022
+KernelVersion:	6.2.0
+Contact:	Linux Kernel Hardening List <[email protected]>
+Description:
+		Shows how many times the system has Warned since last boot.
kernel/panic.c +20 -2
index bfe6e4c5cd0e7..8f7bf0a8cef1a 100644
@@ -28,6 +28,7 @@
 #include <linux/console.h>
 #include <linux/bug.h>
 #include <linux/ratelimit.h>
+#include <linux/sysfs.h>
 
 #define PANIC_TIMER_STEP 100
 #define PANIC_BLINK_SPD 18
@@ -68,6 +69,25 @@ static __init int kernel_panic_sysctls_init(void)
 late_initcall(kernel_panic_sysctls_init);
 #endif
 
+static atomic_t warn_count = ATOMIC_INIT(0);
+
+#ifdef CONFIG_SYSFS
+static ssize_t warn_count_show(struct kobject *kobj, struct kobj_attribute *attr,
+			       char *page)
+{
+	return sysfs_emit(page, "%d\n", atomic_read(&warn_count));
+}
+
+static struct kobj_attribute warn_count_attr = __ATTR_RO(warn_count);
+
+static __init int kernel_panic_sysfs_init(void)
+{
+	sysfs_add_file_to_group(kernel_kobj, &warn_count_attr.attr, NULL);
+	return 0;
+}
+late_initcall(kernel_panic_sysfs_init);
+#endif
+
 static long no_blink(int state)
 {
 	return 0;
@@ -145,8 +165,6 @@ EXPORT_SYMBOL(nmi_panic);
 
 void check_panic_on_warn(const char *origin)
 {
-	static atomic_t warn_count = ATOMIC_INIT(0);
-
 	if (panic_on_warn)
 		panic("%s: panic_on_warn set ...\n", origin);
 
Signature · 1 line
2.39.1
#15 lore raw

PATCH v2 14/15 docs: Fix path paste-o for /sys/kernel/warn_count

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 00dd027f721e0458418f7750d8a5a664ed3e5994 upstream.

Running "make htmldocs" shows that "/sys/kernel/oops_count" was duplicated. This should have been "warn_count":

Warning: /sys/kernel/oops_count is defined 2 times:
./Documentation/ABI/testing/sysfs-kernel-warn_count:0 ./Documentation/ABI/testing/sysfs-kernel-oops_count:0

Fix the typo.

---
 Documentation/ABI/testing/sysfs-kernel-warn_count | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Documentation/ABI/testing/sysfs-kernel-warn_count +1 -1
index 08f083d2fd51b..90a029813717d 100644
@@ -1,4 +1,4 @@
-What:		/sys/kernel/oops_count
+What:		/sys/kernel/warn_count
 Date:		November 2022
 KernelVersion:	6.2.0
 Contact:	Linux Kernel Hardening List <[email protected]>
Signature · 1 line
2.39.1
#16 lore raw

PATCH v2 15/15 exit: Use READ_ONCE() for all oops/warn limit reads

In reply to Eric Biggers

From: Kees Cook <[email protected]>

commit 7535b832c6399b5ebfc5b53af5c51dd915ee2538 upstream.

Use a temporary variable to take full advantage of READ_ONCE() behavior. Without this, the report (and even the test) might be out of sync with the initial test.

Fixes 9fc9e278a5c0 ("panic: Introduce warn_limit")
Fixes d4ccd54d28d3 ("exit: Put an upper limit on how often we can oops")
Cc "Eric W. Biederman" <[email protected]>
Cc Jann Horn <[email protected]>
Cc Arnd Bergmann <[email protected]>
Cc Petr Mladek <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Luis Chamberlain <[email protected]>
Cc Marco Elver <[email protected]>
Cc tangmeng <[email protected]>
Cc Sebastian Andrzej Siewior <[email protected]>
Cc Tiezhu Yang <[email protected]>
---
 kernel/exit.c  | 6 ++++--
 kernel/panic.c | 7 +++++--
 2 files changed, 9 insertions(+), 4 deletions(-)
kernel/exit.c +4 -2
index 73103e008a627..c0ff4461643d0 100644
@@ -973,6 +973,7 @@ void __noreturn make_task_dead(int signr)
 	 * Take the task off the cpu after something catastrophic has
 	 * happened.
 	 */
+	unsigned int limit;
 
 	/*
 	 * Every time the system oopses, if the oops happens while a reference
@@ -984,8 +985,9 @@ void __noreturn make_task_dead(int signr)
 	 * 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) && oops_limit)
-		panic("Oopsed too often (kernel.oops_limit is %d)", oops_limit);
+	limit = READ_ONCE(oops_limit);
+	if (atomic_inc_return(&oops_count) >= limit && limit)
+		panic("Oopsed too often (kernel.oops_limit is %d)", limit);
 
 	do_exit(signr);
 }
kernel/panic.c +5 -2
index 8f7bf0a8cef1a..165fd747cebd0 100644
@@ -165,12 +165,15 @@ EXPORT_SYMBOL(nmi_panic);
 
 void check_panic_on_warn(const char *origin)
 {
+	unsigned int limit;
+
 	if (panic_on_warn)
 		panic("%s: panic_on_warn set ...\n", origin);
 
-	if (atomic_inc_return(&warn_count) >= READ_ONCE(warn_limit) && warn_limit)
+	limit = READ_ONCE(warn_limit);
+	if (atomic_inc_return(&warn_count) >= limit && limit)
 		panic("%s: system warned too often (kernel.warn_limit is %d)",
-		      origin, warn_limit);
+		      origin, limit);
 }
 
 /**
Signature · 1 line
2.39.1
#17 lore raw
In reply to Eric Biggers
Quoted 9 lines · Eric Biggers

This series backports the patchset
"exit: Put an upper limit on how often we can oops"
(https://lore.kernel.org/linux-mm/[email protected]/T/#u)
to 4.14, as recommended at
https://googleprojectzero.blogspot.com/2023/01/exploiting-null-dereferences-in-linux.html

Changed in v2:
- Fixed a build error in mm/kasan/report.c by dropping the patch "mm:
kasan: do not panic if both panic_on_warn and kasan_multishot set".

Both v2 series now queued up, let's see if they build...

thanks,

greg k-h