Skip to content

Commit 1eb085d

Browse files
willdeaconIngo Molnar
authored andcommitted
locking/refcount: Move saturation warnings out of line
Having the refcount saturation and warnings inline bloats the text, despite the fact that these paths should never be executed in normal operation. Move the refcount saturation and warnings out of line to reduce the image size when refcount_t checking is enabled. Relative to an x86_64 defconfig, the sizes reported by bloat-o-meter are: # defconfig+REFCOUNT_FULL, inline saturation (i.e. before this patch) Total: Before=14762076, After=14915442, chg +1.04% # defconfig+REFCOUNT_FULL, out-of-line saturation (i.e. after this patch) Total: Before=14762076, After=14835497, chg +0.50% A side-effect of this change is that we now only get one warning per refcount saturation type, rather than one per problematic call-site. Signed-off-by: Will Deacon <[email protected]> Reviewed-by: Ard Biesheuvel <[email protected]> Reviewed-by: Kees Cook <[email protected]> Tested-by: Hanjun Guo <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Elena Reshetova <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent dcb7864 commit 1eb085d

File tree

2 files changed

+48
-19
lines changed

2 files changed

+48
-19
lines changed

include/linux/refcount.h

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ typedef struct refcount_struct {
2323

2424
#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
2525

26+
enum refcount_saturation_type {
27+
REFCOUNT_ADD_NOT_ZERO_OVF,
28+
REFCOUNT_ADD_OVF,
29+
REFCOUNT_ADD_UAF,
30+
REFCOUNT_SUB_UAF,
31+
REFCOUNT_DEC_LEAK,
32+
};
33+
34+
void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t);
35+
2636
/**
2737
* refcount_set - set a refcount's value
2838
* @r: the refcount
@@ -154,10 +164,8 @@ static inline __must_check bool refcount_add_not_zero(int i, refcount_t *r)
154164
break;
155165
} while (!atomic_try_cmpxchg_relaxed(&r->refs, &old, old + i));
156166

157-
if (unlikely(old < 0 || old + i < 0)) {
158-
refcount_set(r, REFCOUNT_SATURATED);
159-
WARN_ONCE(1, "refcount_t: saturated; leaking memory.\n");
160-
}
167+
if (unlikely(old < 0 || old + i < 0))
168+
refcount_warn_saturate(r, REFCOUNT_ADD_NOT_ZERO_OVF);
161169

162170
return old;
163171
}
@@ -182,11 +190,10 @@ static inline void refcount_add(int i, refcount_t *r)
182190
{
183191
int old = atomic_fetch_add_relaxed(i, &r->refs);
184192

185-
WARN_ONCE(!old, "refcount_t: addition on 0; use-after-free.\n");
186-
if (unlikely(old <= 0 || old + i <= 0)) {
187-
refcount_set(r, REFCOUNT_SATURATED);
188-
WARN_ONCE(old, "refcount_t: saturated; leaking memory.\n");
189-
}
193+
if (unlikely(!old))
194+
refcount_warn_saturate(r, REFCOUNT_ADD_UAF);
195+
else if (unlikely(old < 0 || old + i < 0))
196+
refcount_warn_saturate(r, REFCOUNT_ADD_OVF);
190197
}
191198

192199
/**
@@ -253,10 +260,8 @@ static inline __must_check bool refcount_sub_and_test(int i, refcount_t *r)
253260
return true;
254261
}
255262

256-
if (unlikely(old < 0 || old - i < 0)) {
257-
refcount_set(r, REFCOUNT_SATURATED);
258-
WARN_ONCE(1, "refcount_t: underflow; use-after-free.\n");
259-
}
263+
if (unlikely(old < 0 || old - i < 0))
264+
refcount_warn_saturate(r, REFCOUNT_SUB_UAF);
260265

261266
return false;
262267
}
@@ -291,12 +296,8 @@ static inline __must_check bool refcount_dec_and_test(refcount_t *r)
291296
*/
292297
static inline void refcount_dec(refcount_t *r)
293298
{
294-
int old = atomic_fetch_sub_release(1, &r->refs);
295-
296-
if (unlikely(old <= 1)) {
297-
refcount_set(r, REFCOUNT_SATURATED);
298-
WARN_ONCE(1, "refcount_t: decrement hit 0; leaking memory.\n");
299-
}
299+
if (unlikely(atomic_fetch_sub_release(1, &r->refs) <= 1))
300+
refcount_warn_saturate(r, REFCOUNT_DEC_LEAK);
300301
}
301302
#else /* CONFIG_REFCOUNT_FULL */
302303

lib/refcount.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,34 @@
88
#include <linux/spinlock.h>
99
#include <linux/bug.h>
1010

11+
#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
12+
13+
void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
14+
{
15+
refcount_set(r, REFCOUNT_SATURATED);
16+
17+
switch (t) {
18+
case REFCOUNT_ADD_NOT_ZERO_OVF:
19+
REFCOUNT_WARN("saturated; leaking memory");
20+
break;
21+
case REFCOUNT_ADD_OVF:
22+
REFCOUNT_WARN("saturated; leaking memory");
23+
break;
24+
case REFCOUNT_ADD_UAF:
25+
REFCOUNT_WARN("addition on 0; use-after-free");
26+
break;
27+
case REFCOUNT_SUB_UAF:
28+
REFCOUNT_WARN("underflow; use-after-free");
29+
break;
30+
case REFCOUNT_DEC_LEAK:
31+
REFCOUNT_WARN("decrement hit 0; leaking memory");
32+
break;
33+
default:
34+
REFCOUNT_WARN("unknown saturation event!?");
35+
}
36+
}
37+
EXPORT_SYMBOL(refcount_warn_saturate);
38+
1139
/**
1240
* refcount_dec_if_one - decrement a refcount if it is 1
1341
* @r: the refcount

0 commit comments

Comments
 (0)