summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2017-06-21 16:00:26 -0400
committerIngo Molnar <mingo@kernel.org>2017-06-28 12:54:46 -0400
commitfd25d19f6b8da315332bb75936605fb45d3ea981 (patch)
tree526af3a06699d0a2570b04402548a6edc61c07bf
parentf9e169883164390a15b56d00cb7e22c2e72f4dba (diff)
locking/refcount: Create unchecked atomic_t implementation
Many subsystems will not use refcount_t unless there is a way to build the kernel so that there is no regression in speed compared to atomic_t. This adds CONFIG_REFCOUNT_FULL to enable the full refcount_t implementation which has the validation but is slightly slower. When not enabled, refcount_t uses the basic unchecked atomic_t routines, which results in no code changes compared to just using atomic_t directly. Signed-off-by: Kees Cook <keescook@chromium.org> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Alexey Dobriyan <adobriyan@gmail.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Christoph Hellwig <hch@infradead.org> Cc: David S. Miller <davem@davemloft.net> Cc: David Windsor <dwindsor@gmail.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Elena Reshetova <elena.reshetova@intel.com> Cc: Eric Biggers <ebiggers3@gmail.com> Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Hans Liljestrand <ishkamiel@gmail.com> Cc: James Bottomley <James.Bottomley@hansenpartnership.com> Cc: Jann Horn <jannh@google.com> Cc: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Manfred Spraul <manfred@colorfullife.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@redhat.com> Cc: Serge E. Hallyn <serge@hallyn.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: arozansk@redhat.com Cc: axboe@kernel.dk Cc: linux-arch <linux-arch@vger.kernel.org> Link: http://lkml.kernel.org/r/20170621200026.GA115679@beast Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/Kconfig9
-rw-r--r--include/linux/refcount.h42
-rw-r--r--lib/refcount.c3
3 files changed, 54 insertions, 0 deletions
diff --git a/arch/Kconfig b/arch/Kconfig
index 6c00e5b00f8b..f76b214cf7ad 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -867,4 +867,13 @@ config STRICT_MODULE_RWX
867config ARCH_WANT_RELAX_ORDER 867config ARCH_WANT_RELAX_ORDER
868 bool 868 bool
869 869
870config REFCOUNT_FULL
871 bool "Perform full reference count validation at the expense of speed"
872 help
873 Enabling this switches the refcounting infrastructure from a fast
874 unchecked atomic_t implementation to a fully state checked
875 implementation, which can be (slightly) slower but provides protections
876 against various use-after-free conditions that can be used in
877 security flaw exploits.
878
870source "kernel/gcov/Kconfig" 879source "kernel/gcov/Kconfig"
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index b34aa649d204..bb71f2871dac 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -41,6 +41,7 @@ static inline unsigned int refcount_read(const refcount_t *r)
41 return atomic_read(&r->refs); 41 return atomic_read(&r->refs);
42} 42}
43 43
44#ifdef CONFIG_REFCOUNT_FULL
44extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r); 45extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r);
45extern void refcount_add(unsigned int i, refcount_t *r); 46extern void refcount_add(unsigned int i, refcount_t *r);
46 47
@@ -52,6 +53,47 @@ extern void refcount_sub(unsigned int i, refcount_t *r);
52 53
53extern __must_check bool refcount_dec_and_test(refcount_t *r); 54extern __must_check bool refcount_dec_and_test(refcount_t *r);
54extern void refcount_dec(refcount_t *r); 55extern void refcount_dec(refcount_t *r);
56#else
57static inline __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r)
58{
59 return atomic_add_unless(&r->refs, i, 0);
60}
61
62static inline void refcount_add(unsigned int i, refcount_t *r)
63{
64 atomic_add(i, &r->refs);
65}
66
67static inline __must_check bool refcount_inc_not_zero(refcount_t *r)
68{
69 return atomic_add_unless(&r->refs, 1, 0);
70}
71
72static inline void refcount_inc(refcount_t *r)
73{
74 atomic_inc(&r->refs);
75}
76
77static inline __must_check bool refcount_sub_and_test(unsigned int i, refcount_t *r)
78{
79 return atomic_sub_and_test(i, &r->refs);
80}
81
82static inline void refcount_sub(unsigned int i, refcount_t *r)
83{
84 atomic_sub(i, &r->refs);
85}
86
87static inline __must_check bool refcount_dec_and_test(refcount_t *r)
88{
89 return atomic_dec_and_test(&r->refs);
90}
91
92static inline void refcount_dec(refcount_t *r)
93{
94 atomic_dec(&r->refs);
95}
96#endif /* CONFIG_REFCOUNT_FULL */
55 97
56extern __must_check bool refcount_dec_if_one(refcount_t *r); 98extern __must_check bool refcount_dec_if_one(refcount_t *r);
57extern __must_check bool refcount_dec_not_one(refcount_t *r); 99extern __must_check bool refcount_dec_not_one(refcount_t *r);
diff --git a/lib/refcount.c b/lib/refcount.c
index 9f906783987e..5d0582a9480c 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -37,6 +37,8 @@
37#include <linux/refcount.h> 37#include <linux/refcount.h>
38#include <linux/bug.h> 38#include <linux/bug.h>
39 39
40#ifdef CONFIG_REFCOUNT_FULL
41
40/** 42/**
41 * refcount_add_not_zero - add a value to a refcount unless it is 0 43 * refcount_add_not_zero - add a value to a refcount unless it is 0
42 * @i: the value to add to the refcount 44 * @i: the value to add to the refcount
@@ -225,6 +227,7 @@ void refcount_dec(refcount_t *r)
225 WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n"); 227 WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
226} 228}
227EXPORT_SYMBOL(refcount_dec); 229EXPORT_SYMBOL(refcount_dec);
230#endif /* CONFIG_REFCOUNT_FULL */
228 231
229/** 232/**
230 * refcount_dec_if_one - decrement a refcount if it is 1 233 * refcount_dec_if_one - decrement a refcount if it is 1