All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: stable@vger.kernel.org
Cc: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>,
	Kees Cook <keescook@chromium.org>, SeongJae Park <sj@kernel.org>,
	Seth Jenkins <sethjenkins@google.com>,
	Jann Horn <jannh@google.com>,
	"Eric W . Biederman" <ebiederm@xmission.com>,
	linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org,
	Luis Chamberlain <mcgrof@kernel.org>
Subject: [PATCH 4.19 09/16] exit: Put an upper limit on how often we can oops
Date: Wed,  1 Feb 2023 21:25:57 -0800	[thread overview]
Message-ID: <20230202052604.179184-10-ebiggers@kernel.org> (raw)
In-Reply-To: <20230202052604.179184-1-ebiggers@kernel.org>

From: Jann Horn <jannh@google.com>

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.

Signed-off-by: Jann Horn <jannh@google.com>
Link: https://lore.kernel.org/r/20221107201317.324457-1-jannh@google.com
Reviewed-by: Luis Chamberlain <mcgrof@kernel.org>
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: https://lore.kernel.org/r/20221117234328.594699-2-keescook@chromium.org
Signed-off-by: Eric Biggers <ebiggers@google.com>
---
 Documentation/sysctl/kernel.txt |  9 +++++++
 kernel/exit.c                   | 43 +++++++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)

diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index db1676525ca35..fd65f4e651d55 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -51,6 +51,7 @@ show up in /proc/sys/kernel:
 - msgmnb
 - msgmni
 - nmi_watchdog
+- oops_limit
 - osrelease
 - ostype
 - overflowgid
@@ -555,6 +556,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
diff --git a/kernel/exit.c b/kernel/exit.c
index 2147bbc2c7c32..fa3004ff33362 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -68,6 +68,33 @@
 #include <asm/pgtable.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;
+
+#ifdef 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--;
@@ -924,10 +951,26 @@ 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.
 	 */
+
+	/*
+	 * 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 (kernel.oops_limit is %d)", oops_limit);
+
 	do_exit(signr);
 }
 
-- 
2.39.1


  parent reply	other threads:[~2023-02-02  5:28 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-02-02  5:25 [PATCH 4.19 00/16] Backport oops_limit to 4.19 Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 01/16] sysctl: add a new register_sysctl_init() interface Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 02/16] panic: unset panic_on_warn inside panic() Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 03/16] mm: kasan: do not panic if both panic_on_warn and kasan_multishot set Eric Biggers
2023-02-02 19:37   ` SeongJae Park
2023-02-02  5:25 ` [PATCH 4.19 04/16] exit: Add and use make_task_dead Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 05/16] objtool: Add a missing comma to avoid string concatenation Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 06/16] hexagon: Fix function name in die() Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 07/16] h8300: Fix build errors from do_exit() to make_task_dead() transition Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 08/16] ia64: make IA64_MCA_RECOVERY bool instead of tristate Eric Biggers
2023-02-02  5:25 ` Eric Biggers [this message]
2023-02-02  5:25 ` [PATCH 4.19 10/16] exit: Expose "oops_count" to sysfs Eric Biggers
2023-02-02  5:25 ` [PATCH 4.19 11/16] exit: Allow oops_limit to be disabled Eric Biggers
2023-02-02  5:26 ` [PATCH 4.19 12/16] panic: Consolidate open-coded panic_on_warn checks Eric Biggers
2023-02-02  5:26 ` [PATCH 4.19 13/16] panic: Introduce warn_limit Eric Biggers
2023-02-02  5:26 ` [PATCH 4.19 14/16] panic: Expose "warn_count" to sysfs Eric Biggers
2023-02-02  5:26 ` [PATCH 4.19 15/16] docs: Fix path paste-o for /sys/kernel/warn_count Eric Biggers
2023-02-02  5:26 ` [PATCH 4.19 16/16] exit: Use READ_ONCE() for all oops/warn limit reads Eric Biggers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230202052604.179184-10-ebiggers@kernel.org \
    --to=ebiggers@kernel.org \
    --cc=ebiederm@xmission.com \
    --cc=harshit.m.mogalapalli@oracle.com \
    --cc=jannh@google.com \
    --cc=keescook@chromium.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mcgrof@kernel.org \
    --cc=sethjenkins@google.com \
    --cc=sj@kernel.org \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.