1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Out-of-line refcount functions.
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
13 #ifdef CONFIG_BUG_ON_DATA_CORRUPTION
14 #define REFCOUNT_CORRUPTION(str) ({ \
15 pr_err("refcount_t: " str ".\n"); \
16 BUG(); \
17 })
18 #else
19 #define REFCOUNT_CORRUPTION(str) REFCOUNT_WARN(str)
20 #endif
21
refcount_warn_saturate(refcount_t * r,enum refcount_saturation_type t)22 void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
23 {
24 refcount_set(r, REFCOUNT_SATURATED);
25
26 switch (t) {
27 case REFCOUNT_ADD_NOT_ZERO_OVF:
28 REFCOUNT_WARN("saturated; leaking memory");
29 break;
30 case REFCOUNT_ADD_OVF:
31 REFCOUNT_WARN("saturated; leaking memory");
32 break;
33 case REFCOUNT_ADD_UAF:
34 REFCOUNT_CORRUPTION("addition on 0; use-after-free");
35 break;
36 case REFCOUNT_SUB_UAF:
37 REFCOUNT_CORRUPTION("underflow; use-after-free");
38 break;
39 case REFCOUNT_DEC_LEAK:
40 REFCOUNT_WARN("decrement hit 0; leaking memory");
41 break;
42 default:
43 REFCOUNT_WARN("unknown saturation event!?");
44 }
45 }
46 EXPORT_SYMBOL(refcount_warn_saturate);
47
48 /**
49 * refcount_dec_if_one - decrement a refcount if it is 1
50 * @r: the refcount
51 *
52 * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
53 * success thereof.
54 *
55 * Like all decrement operations, it provides release memory order and provides
56 * a control dependency.
57 *
58 * It can be used like a try-delete operator; this explicit case is provided
59 * and not cmpxchg in generic, because that would allow implementing unsafe
60 * operations.
61 *
62 * Return: true if the resulting refcount is 0, false otherwise
63 */
refcount_dec_if_one(refcount_t * r)64 bool refcount_dec_if_one(refcount_t *r)
65 {
66 int val = 1;
67
68 return atomic_try_cmpxchg_release(&r->refs, &val, 0);
69 }
70 EXPORT_SYMBOL(refcount_dec_if_one);
71
72 /**
73 * refcount_dec_not_one - decrement a refcount if it is not 1
74 * @r: the refcount
75 *
76 * No atomic_t counterpart, it decrements unless the value is 1, in which case
77 * it will return false.
78 *
79 * Was often done like: atomic_add_unless(&var, -1, 1)
80 *
81 * Return: true if the decrement operation was successful, false otherwise
82 */
refcount_dec_not_one(refcount_t * r)83 bool refcount_dec_not_one(refcount_t *r)
84 {
85 unsigned int new, val = atomic_read(&r->refs);
86
87 do {
88 if (unlikely(val == REFCOUNT_SATURATED))
89 return true;
90
91 if (val == 1)
92 return false;
93
94 new = val - 1;
95 if (new > val) {
96 REFCOUNT_CORRUPTION("underflow; use-after-free");
97 return true;
98 }
99
100 } while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
101
102 return true;
103 }
104 EXPORT_SYMBOL(refcount_dec_not_one);
105
106 /**
107 * refcount_dec_and_mutex_lock - return holding mutex if able to decrement
108 * refcount to 0
109 * @r: the refcount
110 * @lock: the mutex to be locked
111 *
112 * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
113 * to decrement when saturated at REFCOUNT_SATURATED.
114 *
115 * Provides release memory ordering, such that prior loads and stores are done
116 * before, and provides a control dependency such that free() must come after.
117 * See the comment on top.
118 *
119 * Return: true and hold mutex if able to decrement refcount to 0, false
120 * otherwise
121 */
refcount_dec_and_mutex_lock(refcount_t * r,struct mutex * lock)122 bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
123 {
124 if (refcount_dec_not_one(r))
125 return false;
126
127 mutex_lock(lock);
128 if (!refcount_dec_and_test(r)) {
129 mutex_unlock(lock);
130 return false;
131 }
132
133 return true;
134 }
135 EXPORT_SYMBOL(refcount_dec_and_mutex_lock);
136
137 /**
138 * refcount_dec_and_lock - return holding spinlock if able to decrement
139 * refcount to 0
140 * @r: the refcount
141 * @lock: the spinlock to be locked
142 *
143 * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
144 * decrement when saturated at REFCOUNT_SATURATED.
145 *
146 * Provides release memory ordering, such that prior loads and stores are done
147 * before, and provides a control dependency such that free() must come after.
148 * See the comment on top.
149 *
150 * Return: true and hold spinlock if able to decrement refcount to 0, false
151 * otherwise
152 */
refcount_dec_and_lock(refcount_t * r,spinlock_t * lock)153 bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
154 {
155 if (refcount_dec_not_one(r))
156 return false;
157
158 spin_lock(lock);
159 if (!refcount_dec_and_test(r)) {
160 spin_unlock(lock);
161 return false;
162 }
163
164 return true;
165 }
166 EXPORT_SYMBOL(refcount_dec_and_lock);
167
168 /**
169 * refcount_dec_and_lock_irqsave - return holding spinlock with disabled
170 * interrupts if able to decrement refcount to 0
171 * @r: the refcount
172 * @lock: the spinlock to be locked
173 * @flags: saved IRQ-flags if the is acquired
174 *
175 * Same as refcount_dec_and_lock() above except that the spinlock is acquired
176 * with disabled interrupts.
177 *
178 * Return: true and hold spinlock if able to decrement refcount to 0, false
179 * otherwise
180 */
refcount_dec_and_lock_irqsave(refcount_t * r,spinlock_t * lock,unsigned long * flags)181 bool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock,
182 unsigned long *flags)
183 {
184 if (refcount_dec_not_one(r))
185 return false;
186
187 spin_lock_irqsave(lock, *flags);
188 if (!refcount_dec_and_test(r)) {
189 spin_unlock_irqrestore(lock, *flags);
190 return false;
191 }
192
193 return true;
194 }
195 EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);
196