History

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.

Will Deacon committed 6 years ago

Commit 1eb085d1 parent dcb78642 files · +48 −19Browse files

Unified · Split

2 files
  1. include/linux/refcount.h +20 −19
  2. lib/refcount.c +28 −0
include/linux/refcount.h+20−19
@@ -23,6 +23,16 @@ typedef struct refcount_struct { · 22 unmodified lines
23 23
24#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }24#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
25 25
26enum 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
34void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t);
35
26/**36/**
27 * refcount_set - set a refcount's value37 * refcount_set - set a refcount's value
28 * @r: the refcount38 * @r: the refcount
@@ -154,10 +164,8 @@ static inline __must_check bool refcount_add_not_zero(int i, refcount_t *r) · 125 unmodified lines
154 break;164 break;
155 } while (!atomic_try_cmpxchg_relaxed(&r->refs, &old, old + i));165 } while (!atomic_try_cmpxchg_relaxed(&r->refs, &old, old + i));
156 166
157 if (unlikely(old < 0 || old + i < 0)) {167 if (unlikely(old < 0 || old + i < 0))
158 refcount_set(r, REFCOUNT_SATURATED);168 refcount_warn_saturate(r, REFCOUNT_ADD_NOT_ZERO_OVF);
159 WARN_ONCE(1, "refcount_t: saturated; leaking memory.\n");
160 }
161 169
162 return old;170 return old;
163}171}
@@ -182,11 +190,10 @@ static inline void refcount_add(int i, refcount_t *r) · 18 unmodified lines
182{190{
183 int old = atomic_fetch_add_relaxed(i, &r->refs);191 int old = atomic_fetch_add_relaxed(i, &r->refs);
184 192
185 WARN_ONCE(!old, "refcount_t: addition on 0; use-after-free.\n");193 if (unlikely(!old))
186 if (unlikely(old <= 0 || old + i <= 0)) {194 refcount_warn_saturate(r, REFCOUNT_ADD_UAF);
187 refcount_set(r, REFCOUNT_SATURATED);195 else if (unlikely(old < 0 || old + i < 0))
188 WARN_ONCE(old, "refcount_t: saturated; leaking memory.\n");196 refcount_warn_saturate(r, REFCOUNT_ADD_OVF);
189 }
190}197}
191 198
192/**199/**
@@ -253,10 +260,8 @@ static inline __must_check bool refcount_sub_and_test(int i, refcount_t *r) · 60 unmodified lines
253 return true;260 return true;
254 }261 }
255 262
256 if (unlikely(old < 0 || old - i < 0)) {263 if (unlikely(old < 0 || old - i < 0))
257 refcount_set(r, REFCOUNT_SATURATED);264 refcount_warn_saturate(r, REFCOUNT_SUB_UAF);
258 WARN_ONCE(1, "refcount_t: underflow; use-after-free.\n");
259 }
260 265
261 return false;266 return false;
262}267}
@@ -291,12 +296,8 @@ static inline __must_check bool refcount_dec_and_test(refcount_t *r) · 28 unmodified lines
291 */296 */
292static inline void refcount_dec(refcount_t *r)297static inline void refcount_dec(refcount_t *r)
293{298{
294 int old = atomic_fetch_sub_release(1, &r->refs);299 if (unlikely(atomic_fetch_sub_release(1, &r->refs) <= 1))
295 300 refcount_warn_saturate(r, REFCOUNT_DEC_LEAK);
296 if (unlikely(old <= 1)) {
297 refcount_set(r, REFCOUNT_SATURATED);
298 WARN_ONCE(1, "refcount_t: decrement hit 0; leaking memory.\n");
299 }
300}301}
301#else /* CONFIG_REFCOUNT_FULL */302#else /* CONFIG_REFCOUNT_FULL */
302 303
lib/refcount.c+28−0
@@ -8,6 +8,34 @@ · 7 unmodified lines
8#include <linux/spinlock.h>8#include <linux/spinlock.h>
9#include <linux/bug.h>9#include <linux/bug.h>
10 10
11#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
12
13void 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}
37EXPORT_SYMBOL(refcount_warn_saturate);
38
11/**39/**
12 * refcount_dec_if_one - decrement a refcount if it is 140 * refcount_dec_if_one - decrement a refcount if it is 1
13 * @r: the refcount41 * @r: the refcount

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t