diff --git a/arch/Kconfig b/arch/Kconfig index 03cff3c16b1c..6ba7bbb16c68 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1027,4 +1027,13 @@ config REFCOUNT_FULL against various use-after-free conditions that can be used in security flaw exploits. +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" diff --git a/lib/refcount.c b/lib/refcount.c index 0eb48353abe3..6d0d2fc7de13 100644 --- a/lib/refcount.c +++ b/lib/refcount.c @@ -38,6 +38,18 @@ #include #include +#ifdef CONFIG_PANIC_ON_REFCOUNT_ERROR +#define REFCOUNT_WARN_ONCE(cond, msg) \ +do { \ + if (cond) { \ + printk(msg); \ + BUG(); \ + } \ +} while (0) +#else +#define REFCOUNT_WARN_ONCE(cond, msg) WARN_ONCE(cond, msg) +#endif /* CONFIG_PANIC_ON_REFCOUNT_ERROR */ + #ifdef CONFIG_REFCOUNT_FULL /** @@ -75,7 +87,8 @@ bool refcount_add_not_zero(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 +112,8 @@ EXPORT_SYMBOL(refcount_add_not_zero); */ void refcount_add(unsigned int i, refcount_t *r) { - WARN_ONCE(!refcount_add_not_zero(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); @@ -130,7 +144,8 @@ bool refcount_inc_not_zero(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 +165,8 @@ EXPORT_SYMBOL(refcount_inc_not_zero); */ void refcount_inc(refcount_t *r) { - WARN_ONCE(!refcount_inc_not_zero(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); @@ -184,7 +200,8 @@ bool refcount_sub_and_test(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 +242,8 @@ EXPORT_SYMBOL(refcount_dec_and_test); */ void refcount_dec(refcount_t *r) { - WARN_ONCE(refcount_dec_and_test(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); #endif /* CONFIG_REFCOUNT_FULL */ @@ -278,7 +296,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; }