xref: /openbmc/linux/lib/refcount.c (revision 9f99d983)
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 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 
39 /**
40  * refcount_dec_if_one - decrement a refcount if it is 1
41  * @r: the refcount
42  *
43  * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
44  * success thereof.
45  *
46  * Like all decrement operations, it provides release memory order and provides
47  * a control dependency.
48  *
49  * It can be used like a try-delete operator; this explicit case is provided
50  * and not cmpxchg in generic, because that would allow implementing unsafe
51  * operations.
52  *
53  * Return: true if the resulting refcount is 0, false otherwise
54  */
55 bool refcount_dec_if_one(refcount_t *r)
56 {
57 	int val = 1;
58 
59 	return atomic_try_cmpxchg_release(&r->refs, &val, 0);
60 }
61 EXPORT_SYMBOL(refcount_dec_if_one);
62 
63 /**
64  * refcount_dec_not_one - decrement a refcount if it is not 1
65  * @r: the refcount
66  *
67  * No atomic_t counterpart, it decrements unless the value is 1, in which case
68  * it will return false.
69  *
70  * Was often done like: atomic_add_unless(&var, -1, 1)
71  *
72  * Return: true if the decrement operation was successful, false otherwise
73  */
74 bool 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 }
95 EXPORT_SYMBOL(refcount_dec_not_one);
96 
97 /**
98  * refcount_dec_and_mutex_lock - return holding mutex if able to decrement
99  *                               refcount to 0
100  * @r: the refcount
101  * @lock: the mutex to be locked
102  *
103  * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
104  * to decrement when saturated at REFCOUNT_SATURATED.
105  *
106  * Provides release memory ordering, such that prior loads and stores are done
107  * before, and provides a control dependency such that free() must come after.
108  * See the comment on top.
109  *
110  * Return: true and hold mutex if able to decrement refcount to 0, false
111  *         otherwise
112  */
113 bool 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 }
126 EXPORT_SYMBOL(refcount_dec_and_mutex_lock);
127 
128 /**
129  * refcount_dec_and_lock - return holding spinlock if able to decrement
130  *                         refcount to 0
131  * @r: the refcount
132  * @lock: the spinlock to be locked
133  *
134  * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
135  * decrement when saturated at REFCOUNT_SATURATED.
136  *
137  * Provides release memory ordering, such that prior loads and stores are done
138  * before, and provides a control dependency such that free() must come after.
139  * See the comment on top.
140  *
141  * Return: true and hold spinlock if able to decrement refcount to 0, false
142  *         otherwise
143  */
144 bool 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 }
157 EXPORT_SYMBOL(refcount_dec_and_lock);
158 
159 /**
160  * refcount_dec_and_lock_irqsave - return holding spinlock with disabled
161  *                                 interrupts if able to decrement refcount to 0
162  * @r: the refcount
163  * @lock: the spinlock to be locked
164  * @flags: saved IRQ-flags if the is acquired
165  *
166  * Same as refcount_dec_and_lock() above except that the spinlock is acquired
167  * with disabled interrupts.
168  *
169  * Return: true and hold spinlock if able to decrement refcount to 0, false
170  *         otherwise
171  */
172 bool 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 }
186 EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);
187