From e1bdae2e7a41fe8a9392012ecc84685f6cf19d50 Mon Sep 17 00:00:00 2001 From: "Isaac J. Manjarres" Date: Fri, 4 May 2018 16:02:44 -0700 Subject: [PATCH] lib: refcount: Cause kernel panic on refcount error detection Currently, when using the refcount API functions, a warning is printed out once to let a user of the refcount API know that an error case has been detected. Then the refcount functions will silently return, without modifying the reference count, which could be mistaken for a successful modification. This can allow for improper use of the object associated with that refcount later. Trigger a kernel panic in case of refcount error detection to prevent misuse of objects associated with refcounts. Change-Id: Ifb6a331d08a7d6c285225bc9667d2f4054db3561 Signed-off-by: Isaac J. Manjarres Signed-off-by: Venkata Narendra Kumar Gutta --- arch/Kconfig | 9 +++++++++ lib/refcount.c | 31 ++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/arch/Kconfig b/arch/Kconfig index 3702289387c8..dc96df73651f 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -890,6 +890,15 @@ config HAVE_ARCH_PREL32_RELOCATIONS architectures, and don't require runtime relocation on relocatable kernels. +config PANIC_ON_REFCOUNT_ERROR + bool "Kernel panic on refcount error detection" + depends on REFCOUNT_FULL + help + If enabled, the kernel will panic when the refcount library + has detected any type of error (e.g. potential use-after-free + or potential memory-leaks) with an object associated with that + reference counter. + source "kernel/gcov/Kconfig" source "scripts/gcc-plugins/Kconfig" diff --git a/lib/refcount.c b/lib/refcount.c index ebcf8cd49e05..8ca656c629ac 100644 --- a/lib/refcount.c +++ b/lib/refcount.c @@ -40,6 +40,16 @@ #include #include +#ifdef CONFIG_PANIC_ON_REFCOUNT_ERROR +#define REFCOUNT_WARN_ONCE(cond, msg) \ +do { \ + if (cond) \ + panic(msg); \ +} while (0) +#else +#define REFCOUNT_WARN_ONCE(cond, msg) WARN_ONCE(cond, msg) +#endif /* CONFIG_PANIC_ON_REFCOUNT_ERROR */ + /** * refcount_add_not_zero_checked - add a value to a refcount unless it is 0 * @i: the value to add to the refcount @@ -75,7 +85,8 @@ bool refcount_add_not_zero_checked(unsigned int i, refcount_t *r) } while (!atomic_try_cmpxchg_relaxed(&r->refs, &val, new)); - WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n"); + REFCOUNT_WARN_ONCE(new == UINT_MAX, + "refcount_t: saturated; leaking memory.\n"); return true; } @@ -99,7 +110,8 @@ EXPORT_SYMBOL(refcount_add_not_zero_checked); */ void refcount_add_checked(unsigned int i, refcount_t *r) { - WARN_ONCE(!refcount_add_not_zero_checked(i, r), "refcount_t: addition on 0; use-after-free.\n"); + REFCOUNT_WARN_ONCE(!refcount_add_not_zero(i, r), + "refcount_t: addition on 0; use-after-free.\n"); } EXPORT_SYMBOL(refcount_add_checked); @@ -130,7 +142,8 @@ bool refcount_inc_not_zero_checked(refcount_t *r) } while (!atomic_try_cmpxchg_relaxed(&r->refs, &val, new)); - WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n"); + REFCOUNT_WARN_ONCE(new == UINT_MAX, + "refcount_t: saturated; leaking memory.\n"); return true; } @@ -150,7 +163,8 @@ EXPORT_SYMBOL(refcount_inc_not_zero_checked); */ void refcount_inc_checked(refcount_t *r) { - WARN_ONCE(!refcount_inc_not_zero_checked(r), "refcount_t: increment on 0; use-after-free.\n"); + REFCOUNT_WARN_ONCE(!refcount_inc_not_zero(r), + "refcount_t: increment on 0; use-after-free.\n"); } EXPORT_SYMBOL(refcount_inc_checked); @@ -184,7 +198,8 @@ bool refcount_sub_and_test_checked(unsigned int i, refcount_t *r) new = val - i; if (new > val) { - WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n"); + REFCOUNT_WARN_ONCE(new > val, + "refcount_t: underflow; use-after-free.\n"); return false; } @@ -225,7 +240,8 @@ EXPORT_SYMBOL(refcount_dec_and_test_checked); */ void refcount_dec_checked(refcount_t *r) { - WARN_ONCE(refcount_dec_and_test_checked(r), "refcount_t: decrement hit 0; leaking memory.\n"); + REFCOUNT_WARN_ONCE(refcount_dec_and_test(r), + "refcount_t: decrement hit 0; leaking memory.\n"); } EXPORT_SYMBOL(refcount_dec_checked); @@ -277,7 +293,8 @@ bool refcount_dec_not_one(refcount_t *r) new = val - 1; if (new > val) { - WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n"); + REFCOUNT_WARN_ONCE(new > val, + "refcount_t: underflow; use-after-free.\n"); return true; }