1
2
3
4
5
6#include <linux/mutex.h>
7#include <linux/refcount.h>
8#include <linux/spinlock.h>
9#include <linux/bug.h>
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55bool refcount_dec_if_one(refcount_t *r)
56{
57 int val = 1;
58
59 return atomic_try_cmpxchg_release(&r->refs, &val, 0);
60}
61EXPORT_SYMBOL(refcount_dec_if_one);
62
63
64
65
66
67
68
69
70
71
72
73
74bool refcount_dec_not_one(refcount_t *r)
75{
76 unsigned int new, val = atomic_read(&r->refs);
77
78 do {
79 if (unlikely(val == REFCOUNT_SATURATED))
80 return true;
81
82 if (val == 1)
83 return false;
84
85 new = val - 1;
86 if (new > val) {
87 WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
88 return true;
89 }
90
91 } while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
92
93 return true;
94}
95EXPORT_SYMBOL(refcount_dec_not_one);
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
114{
115 if (refcount_dec_not_one(r))
116 return false;
117
118 mutex_lock(lock);
119 if (!refcount_dec_and_test(r)) {
120 mutex_unlock(lock);
121 return false;
122 }
123
124 return true;
125}
126EXPORT_SYMBOL(refcount_dec_and_mutex_lock);
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
145{
146 if (refcount_dec_not_one(r))
147 return false;
148
149 spin_lock(lock);
150 if (!refcount_dec_and_test(r)) {
151 spin_unlock(lock);
152 return false;
153 }
154
155 return true;
156}
157EXPORT_SYMBOL(refcount_dec_and_lock);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172bool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock,
173 unsigned long *flags)
174{
175 if (refcount_dec_not_one(r))
176 return false;
177
178 spin_lock_irqsave(lock, *flags);
179 if (!refcount_dec_and_test(r)) {
180 spin_unlock_irqrestore(lock, *flags);
181 return false;
182 }
183
184 return true;
185}
186EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);
187