diff options
author | Kees Cook <keescook@chromium.org> | 2017-06-21 16:00:26 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2017-06-28 12:54:46 -0400 |
commit | fd25d19f6b8da315332bb75936605fb45d3ea981 (patch) | |
tree | 526af3a06699d0a2570b04402548a6edc61c07bf | |
parent | f9e169883164390a15b56d00cb7e22c2e72f4dba (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/Kconfig | 9 | ||||
-rw-r--r-- | include/linux/refcount.h | 42 | ||||
-rw-r--r-- | lib/refcount.c | 3 |
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 | |||
867 | config ARCH_WANT_RELAX_ORDER | 867 | config ARCH_WANT_RELAX_ORDER |
868 | bool | 868 | bool |
869 | 869 | ||
870 | config 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 | |||
870 | source "kernel/gcov/Kconfig" | 879 | source "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 | ||
44 | extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r); | 45 | extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r); |
45 | extern void refcount_add(unsigned int i, refcount_t *r); | 46 | extern 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 | ||
53 | extern __must_check bool refcount_dec_and_test(refcount_t *r); | 54 | extern __must_check bool refcount_dec_and_test(refcount_t *r); |
54 | extern void refcount_dec(refcount_t *r); | 55 | extern void refcount_dec(refcount_t *r); |
56 | #else | ||
57 | static 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 | |||
62 | static inline void refcount_add(unsigned int i, refcount_t *r) | ||
63 | { | ||
64 | atomic_add(i, &r->refs); | ||
65 | } | ||
66 | |||
67 | static inline __must_check bool refcount_inc_not_zero(refcount_t *r) | ||
68 | { | ||
69 | return atomic_add_unless(&r->refs, 1, 0); | ||
70 | } | ||
71 | |||
72 | static inline void refcount_inc(refcount_t *r) | ||
73 | { | ||
74 | atomic_inc(&r->refs); | ||
75 | } | ||
76 | |||
77 | static 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 | |||
82 | static inline void refcount_sub(unsigned int i, refcount_t *r) | ||
83 | { | ||
84 | atomic_sub(i, &r->refs); | ||
85 | } | ||
86 | |||
87 | static inline __must_check bool refcount_dec_and_test(refcount_t *r) | ||
88 | { | ||
89 | return atomic_dec_and_test(&r->refs); | ||
90 | } | ||
91 | |||
92 | static inline void refcount_dec(refcount_t *r) | ||
93 | { | ||
94 | atomic_dec(&r->refs); | ||
95 | } | ||
96 | #endif /* CONFIG_REFCOUNT_FULL */ | ||
55 | 97 | ||
56 | extern __must_check bool refcount_dec_if_one(refcount_t *r); | 98 | extern __must_check bool refcount_dec_if_one(refcount_t *r); |
57 | extern __must_check bool refcount_dec_not_one(refcount_t *r); | 99 | extern __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 | } |
227 | EXPORT_SYMBOL(refcount_dec); | 229 | EXPORT_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 |