Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

locking/refcount: Always allow checked forms

In many cases, it would be useful to be able to use the full
sanity-checked refcount helpers regardless of CONFIG_REFCOUNT_FULL,
as this would help to avoid duplicate warnings where callers try to
sanity-check refcount manipulation.

This patch refactors things such that the full refcount helpers were
always built, as refcount_${op}_checked(), such that they can be used
regardless of CONFIG_REFCOUNT_FULL. This will allow code which *always*
wants a checked refcount to opt-in, avoiding the need to duplicate the
logic for warnings.

There should be no functional change as a result of this patch.

Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Acked-by: Kees Cook <keescook@chromium.org>
Acked-by: Will Deacon <will.deacon@arm.com>
Cc: Boqun Feng <boqun.feng@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/20180711093607.1644-1-mark.rutland@arm.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Mark Rutland and committed by
Ingo Molnar
afed7bcf 75a040ff

+45 -35
+20 -7
include/linux/refcount.h
··· 43 43 return atomic_read(&r->refs); 44 44 } 45 45 46 + extern __must_check bool refcount_add_not_zero_checked(unsigned int i, refcount_t *r); 47 + extern void refcount_add_checked(unsigned int i, refcount_t *r); 48 + 49 + extern __must_check bool refcount_inc_not_zero_checked(refcount_t *r); 50 + extern void refcount_inc_checked(refcount_t *r); 51 + 52 + extern __must_check bool refcount_sub_and_test_checked(unsigned int i, refcount_t *r); 53 + 54 + extern __must_check bool refcount_dec_and_test_checked(refcount_t *r); 55 + extern void refcount_dec_checked(refcount_t *r); 56 + 46 57 #ifdef CONFIG_REFCOUNT_FULL 47 - extern __must_check bool refcount_add_not_zero(unsigned int i, refcount_t *r); 48 - extern void refcount_add(unsigned int i, refcount_t *r); 49 58 50 - extern __must_check bool refcount_inc_not_zero(refcount_t *r); 51 - extern void refcount_inc(refcount_t *r); 59 + #define refcount_add_not_zero refcount_add_not_zero_checked 60 + #define refcount_add refcount_add_checked 52 61 53 - extern __must_check bool refcount_sub_and_test(unsigned int i, refcount_t *r); 62 + #define refcount_inc_not_zero refcount_inc_not_zero_checked 63 + #define refcount_inc refcount_inc_checked 54 64 55 - extern __must_check bool refcount_dec_and_test(refcount_t *r); 56 - extern void refcount_dec(refcount_t *r); 65 + #define refcount_sub_and_test refcount_sub_and_test_checked 66 + 67 + #define refcount_dec_and_test refcount_dec_and_test_checked 68 + #define refcount_dec refcount_dec_checked 69 + 57 70 #else 58 71 # ifdef CONFIG_ARCH_HAS_REFCOUNT 59 72 # include <asm/refcount.h>
+25 -28
lib/refcount.c
··· 40 40 #include <linux/spinlock.h> 41 41 #include <linux/bug.h> 42 42 43 - #ifdef CONFIG_REFCOUNT_FULL 44 - 45 43 /** 46 - * refcount_add_not_zero - add a value to a refcount unless it is 0 44 + * refcount_add_not_zero_checked - add a value to a refcount unless it is 0 47 45 * @i: the value to add to the refcount 48 46 * @r: the refcount 49 47 * ··· 58 60 * 59 61 * Return: false if the passed refcount is 0, true otherwise 60 62 */ 61 - bool refcount_add_not_zero(unsigned int i, refcount_t *r) 63 + bool refcount_add_not_zero_checked(unsigned int i, refcount_t *r) 62 64 { 63 65 unsigned int new, val = atomic_read(&r->refs); 64 66 ··· 79 81 80 82 return true; 81 83 } 82 - EXPORT_SYMBOL(refcount_add_not_zero); 84 + EXPORT_SYMBOL(refcount_add_not_zero_checked); 83 85 84 86 /** 85 - * refcount_add - add a value to a refcount 87 + * refcount_add_checked - add a value to a refcount 86 88 * @i: the value to add to the refcount 87 89 * @r: the refcount 88 90 * ··· 97 99 * cases, refcount_inc(), or one of its variants, should instead be used to 98 100 * increment a reference count. 99 101 */ 100 - void refcount_add(unsigned int i, refcount_t *r) 102 + void refcount_add_checked(unsigned int i, refcount_t *r) 101 103 { 102 - WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n"); 104 + WARN_ONCE(!refcount_add_not_zero_checked(i, r), "refcount_t: addition on 0; use-after-free.\n"); 103 105 } 104 - EXPORT_SYMBOL(refcount_add); 106 + EXPORT_SYMBOL(refcount_add_checked); 105 107 106 108 /** 107 - * refcount_inc_not_zero - increment a refcount unless it is 0 109 + * refcount_inc_not_zero_checked - increment a refcount unless it is 0 108 110 * @r: the refcount to increment 109 111 * 110 112 * Similar to atomic_inc_not_zero(), but will saturate at UINT_MAX and WARN. ··· 115 117 * 116 118 * Return: true if the increment was successful, false otherwise 117 119 */ 118 - bool refcount_inc_not_zero(refcount_t *r) 120 + bool refcount_inc_not_zero_checked(refcount_t *r) 119 121 { 120 122 unsigned int new, val = atomic_read(&r->refs); 121 123 ··· 134 136 135 137 return true; 136 138 } 137 - EXPORT_SYMBOL(refcount_inc_not_zero); 139 + EXPORT_SYMBOL(refcount_inc_not_zero_checked); 138 140 139 141 /** 140 - * refcount_inc - increment a refcount 142 + * refcount_inc_checked - increment a refcount 141 143 * @r: the refcount to increment 142 144 * 143 145 * Similar to atomic_inc(), but will saturate at UINT_MAX and WARN. ··· 148 150 * Will WARN if the refcount is 0, as this represents a possible use-after-free 149 151 * condition. 150 152 */ 151 - void refcount_inc(refcount_t *r) 153 + void refcount_inc_checked(refcount_t *r) 152 154 { 153 - WARN_ONCE(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n"); 155 + WARN_ONCE(!refcount_inc_not_zero_checked(r), "refcount_t: increment on 0; use-after-free.\n"); 154 156 } 155 - EXPORT_SYMBOL(refcount_inc); 157 + EXPORT_SYMBOL(refcount_inc_checked); 156 158 157 159 /** 158 - * refcount_sub_and_test - subtract from a refcount and test if it is 0 160 + * refcount_sub_and_test_checked - subtract from a refcount and test if it is 0 159 161 * @i: amount to subtract from the refcount 160 162 * @r: the refcount 161 163 * ··· 174 176 * 175 177 * Return: true if the resulting refcount is 0, false otherwise 176 178 */ 177 - bool refcount_sub_and_test(unsigned int i, refcount_t *r) 179 + bool refcount_sub_and_test_checked(unsigned int i, refcount_t *r) 178 180 { 179 181 unsigned int new, val = atomic_read(&r->refs); 180 182 ··· 192 194 193 195 return !new; 194 196 } 195 - EXPORT_SYMBOL(refcount_sub_and_test); 197 + EXPORT_SYMBOL(refcount_sub_and_test_checked); 196 198 197 199 /** 198 - * refcount_dec_and_test - decrement a refcount and test if it is 0 200 + * refcount_dec_and_test_checked - decrement a refcount and test if it is 0 199 201 * @r: the refcount 200 202 * 201 203 * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to ··· 207 209 * 208 210 * Return: true if the resulting refcount is 0, false otherwise 209 211 */ 210 - bool refcount_dec_and_test(refcount_t *r) 212 + bool refcount_dec_and_test_checked(refcount_t *r) 211 213 { 212 - return refcount_sub_and_test(1, r); 214 + return refcount_sub_and_test_checked(1, r); 213 215 } 214 - EXPORT_SYMBOL(refcount_dec_and_test); 216 + EXPORT_SYMBOL(refcount_dec_and_test_checked); 215 217 216 218 /** 217 - * refcount_dec - decrement a refcount 219 + * refcount_dec_checked - decrement a refcount 218 220 * @r: the refcount 219 221 * 220 222 * Similar to atomic_dec(), it will WARN on underflow and fail to decrement ··· 223 225 * Provides release memory ordering, such that prior loads and stores are done 224 226 * before. 225 227 */ 226 - void refcount_dec(refcount_t *r) 228 + void refcount_dec_checked(refcount_t *r) 227 229 { 228 - WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n"); 230 + WARN_ONCE(refcount_dec_and_test_checked(r), "refcount_t: decrement hit 0; leaking memory.\n"); 229 231 } 230 - EXPORT_SYMBOL(refcount_dec); 231 - #endif /* CONFIG_REFCOUNT_FULL */ 232 + EXPORT_SYMBOL(refcount_dec_checked); 232 233 233 234 /** 234 235 * refcount_dec_if_one - decrement a refcount if it is 1