projects/linux·blob master · refs

195 lines · 5.0 KiB

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
22void 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}
46EXPORT_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 */
64bool refcount_dec_if_one(refcount_t *r)
65{
66 int val = 1;
67
68 return atomic_try_cmpxchg_release(&r->refs, &val, 0);
69}
70EXPORT_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 */
83bool 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}
104EXPORT_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 */
122bool 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}
135EXPORT_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 */
153bool 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}
166EXPORT_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 */
181bool 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}
195EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);