| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ | 
|---|
| 2 | #ifndef _LINUX_FILE_REF_H | 
|---|
| 3 | #define _LINUX_FILE_REF_H | 
|---|
| 4 |  | 
|---|
| 5 | #include <linux/atomic.h> | 
|---|
| 6 | #include <linux/preempt.h> | 
|---|
| 7 | #include <linux/types.h> | 
|---|
| 8 |  | 
|---|
| 9 | /* | 
|---|
| 10 | * file_ref is a reference count implementation specifically for use by | 
|---|
| 11 | * files. It takes inspiration from rcuref but differs in key aspects | 
|---|
| 12 | * such as support for SLAB_TYPESAFE_BY_RCU type caches. | 
|---|
| 13 | * | 
|---|
| 14 | * FILE_REF_ONEREF                FILE_REF_MAXREF | 
|---|
| 15 | * 0x0000000000000000UL      0x7FFFFFFFFFFFFFFFUL | 
|---|
| 16 | * <-------------------valid -------------------> | 
|---|
| 17 | * | 
|---|
| 18 | *                       FILE_REF_SATURATED | 
|---|
| 19 | * 0x8000000000000000UL 0xA000000000000000UL 0xBFFFFFFFFFFFFFFFUL | 
|---|
| 20 | * <-----------------------saturation zone----------------------> | 
|---|
| 21 | * | 
|---|
| 22 | * FILE_REF_RELEASED                   FILE_REF_DEAD | 
|---|
| 23 | * 0xC000000000000000UL         0xE000000000000000UL | 
|---|
| 24 | * <-------------------dead zone-------------------> | 
|---|
| 25 | * | 
|---|
| 26 | * FILE_REF_NOREF | 
|---|
| 27 | * 0xFFFFFFFFFFFFFFFFUL | 
|---|
| 28 | */ | 
|---|
| 29 |  | 
|---|
| 30 | #ifdef CONFIG_64BIT | 
|---|
| 31 | #define FILE_REF_ONEREF		0x0000000000000000UL | 
|---|
| 32 | #define FILE_REF_MAXREF		0x7FFFFFFFFFFFFFFFUL | 
|---|
| 33 | #define FILE_REF_SATURATED	0xA000000000000000UL | 
|---|
| 34 | #define FILE_REF_RELEASED	0xC000000000000000UL | 
|---|
| 35 | #define FILE_REF_DEAD		0xE000000000000000UL | 
|---|
| 36 | #define FILE_REF_NOREF		0xFFFFFFFFFFFFFFFFUL | 
|---|
| 37 | #else | 
|---|
| 38 | #define FILE_REF_ONEREF		0x00000000U | 
|---|
| 39 | #define FILE_REF_MAXREF		0x7FFFFFFFU | 
|---|
| 40 | #define FILE_REF_SATURATED	0xA0000000U | 
|---|
| 41 | #define FILE_REF_RELEASED	0xC0000000U | 
|---|
| 42 | #define FILE_REF_DEAD		0xE0000000U | 
|---|
| 43 | #define FILE_REF_NOREF		0xFFFFFFFFU | 
|---|
| 44 | #endif | 
|---|
| 45 |  | 
|---|
| 46 | typedef struct { | 
|---|
| 47 | #ifdef CONFIG_64BIT | 
|---|
| 48 | atomic64_t refcnt; | 
|---|
| 49 | #else | 
|---|
| 50 | atomic_t refcnt; | 
|---|
| 51 | #endif | 
|---|
| 52 | } file_ref_t; | 
|---|
| 53 |  | 
|---|
| 54 | /** | 
|---|
| 55 | * file_ref_init - Initialize a file reference count | 
|---|
| 56 | * @ref: Pointer to the reference count | 
|---|
| 57 | * @cnt: The initial reference count typically '1' | 
|---|
| 58 | */ | 
|---|
| 59 | static inline void file_ref_init(file_ref_t *ref, unsigned long cnt) | 
|---|
| 60 | { | 
|---|
| 61 | atomic_long_set(v: &ref->refcnt, i: cnt - 1); | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | bool __file_ref_put(file_ref_t *ref, unsigned long cnt); | 
|---|
| 65 |  | 
|---|
| 66 | /** | 
|---|
| 67 | * file_ref_get - Acquire one reference on a file | 
|---|
| 68 | * @ref: Pointer to the reference count | 
|---|
| 69 | * | 
|---|
| 70 | * Similar to atomic_inc_not_zero() but saturates at FILE_REF_MAXREF. | 
|---|
| 71 | * | 
|---|
| 72 | * Provides full memory ordering. | 
|---|
| 73 | * | 
|---|
| 74 | * Return: False if the attempt to acquire a reference failed. This happens | 
|---|
| 75 | *         when the last reference has been put already. True if a reference | 
|---|
| 76 | *         was successfully acquired | 
|---|
| 77 | */ | 
|---|
| 78 | static __always_inline __must_check bool file_ref_get(file_ref_t *ref) | 
|---|
| 79 | { | 
|---|
| 80 | /* | 
|---|
| 81 | * Unconditionally increase the reference count with full | 
|---|
| 82 | * ordering. The saturation and dead zones provide enough | 
|---|
| 83 | * tolerance for this. | 
|---|
| 84 | * | 
|---|
| 85 | * If this indicates negative the file in question the fail can | 
|---|
| 86 | * be freed and immediately reused due to SLAB_TYPSAFE_BY_RCU. | 
|---|
| 87 | * Hence, unconditionally altering the file reference count to | 
|---|
| 88 | * e.g., reset the file reference count back to the middle of | 
|---|
| 89 | * the deadzone risk end up marking someone else's file as dead | 
|---|
| 90 | * behind their back. | 
|---|
| 91 | * | 
|---|
| 92 | * It would be possible to do a careful: | 
|---|
| 93 | * | 
|---|
| 94 | * cnt = atomic_long_inc_return(); | 
|---|
| 95 | * if (likely(cnt >= 0)) | 
|---|
| 96 | *	return true; | 
|---|
| 97 | * | 
|---|
| 98 | * and then something like: | 
|---|
| 99 | * | 
|---|
| 100 | * if (cnt >= FILE_REF_RELEASE) | 
|---|
| 101 | *	atomic_long_try_cmpxchg(&ref->refcnt, &cnt, FILE_REF_DEAD), | 
|---|
| 102 | * | 
|---|
| 103 | * to set the value back to the middle of the deadzone. But it's | 
|---|
| 104 | * practically impossible to go from FILE_REF_DEAD to | 
|---|
| 105 | * FILE_REF_ONEREF. It would need 2305843009213693952/2^61 | 
|---|
| 106 | * file_ref_get()s to resurrect such a dead file. | 
|---|
| 107 | */ | 
|---|
| 108 | return !atomic_long_add_negative(i: 1, v: &ref->refcnt); | 
|---|
| 109 | } | 
|---|
| 110 |  | 
|---|
| 111 | /** | 
|---|
| 112 | * file_ref_inc - Acquire one reference on a file | 
|---|
| 113 | * @ref: Pointer to the reference count | 
|---|
| 114 | * | 
|---|
| 115 | * Acquire an additional reference on a file. Warns if the caller didn't | 
|---|
| 116 | * already hold a reference. | 
|---|
| 117 | */ | 
|---|
| 118 | static __always_inline void file_ref_inc(file_ref_t *ref) | 
|---|
| 119 | { | 
|---|
| 120 | long prior = atomic_long_fetch_inc_relaxed(v: &ref->refcnt); | 
|---|
| 121 | WARN_ONCE(prior < 0, "file_ref_inc() on a released file reference"); | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | /** | 
|---|
| 125 | * file_ref_put -- Release a file reference | 
|---|
| 126 | * @ref:	Pointer to the reference count | 
|---|
| 127 | * | 
|---|
| 128 | * Provides release memory ordering, such that prior loads and stores | 
|---|
| 129 | * are done before, and provides an acquire ordering on success such | 
|---|
| 130 | * that free() must come after. | 
|---|
| 131 | * | 
|---|
| 132 | * Return: True if this was the last reference with no future references | 
|---|
| 133 | *         possible. This signals the caller that it can safely release | 
|---|
| 134 | *         the object which is protected by the reference counter. | 
|---|
| 135 | *         False if there are still active references or the put() raced | 
|---|
| 136 | *         with a concurrent get()/put() pair. Caller is not allowed to | 
|---|
| 137 | *         release the protected object. | 
|---|
| 138 | */ | 
|---|
| 139 | static __always_inline __must_check bool file_ref_put(file_ref_t *ref) | 
|---|
| 140 | { | 
|---|
| 141 | long cnt; | 
|---|
| 142 |  | 
|---|
| 143 | /* | 
|---|
| 144 | * While files are SLAB_TYPESAFE_BY_RCU and thus file_ref_put() | 
|---|
| 145 | * calls don't risk UAFs when a file is recyclyed, it is still | 
|---|
| 146 | * vulnerable to UAFs caused by freeing the whole slab page once | 
|---|
| 147 | * it becomes unused. Prevent file_ref_put() from being | 
|---|
| 148 | * preempted protects against this. | 
|---|
| 149 | */ | 
|---|
| 150 | guard(preempt)(); | 
|---|
| 151 | /* | 
|---|
| 152 | * Unconditionally decrease the reference count. The saturation | 
|---|
| 153 | * and dead zones provide enough tolerance for this. If this | 
|---|
| 154 | * fails then we need to handle the last reference drop and | 
|---|
| 155 | * cases inside the saturation and dead zones. | 
|---|
| 156 | */ | 
|---|
| 157 | cnt = atomic_long_dec_return(v: &ref->refcnt); | 
|---|
| 158 | if (cnt >= 0) | 
|---|
| 159 | return false; | 
|---|
| 160 | return __file_ref_put(ref, cnt); | 
|---|
| 161 | } | 
|---|
| 162 |  | 
|---|
| 163 | /** | 
|---|
| 164 | * file_ref_put_close - drop a reference expecting it would transition to FILE_REF_NOREF | 
|---|
| 165 | * @ref:	Pointer to the reference count | 
|---|
| 166 | * | 
|---|
| 167 | * Semantically it is equivalent to calling file_ref_put(), but it trades lower | 
|---|
| 168 | * performance in face of other CPUs also modifying the refcount for higher | 
|---|
| 169 | * performance when this happens to be the last reference. | 
|---|
| 170 | * | 
|---|
| 171 | * For the last reference file_ref_put() issues 2 atomics. One to drop the | 
|---|
| 172 | * reference and another to transition it to FILE_REF_DEAD. This routine does | 
|---|
| 173 | * the work in one step, but in order to do it has to pre-read the variable which | 
|---|
| 174 | * decreases scalability. | 
|---|
| 175 | * | 
|---|
| 176 | * Use with close() et al, stick to file_ref_put() by default. | 
|---|
| 177 | */ | 
|---|
| 178 | static __always_inline __must_check bool file_ref_put_close(file_ref_t *ref) | 
|---|
| 179 | { | 
|---|
| 180 | long old; | 
|---|
| 181 |  | 
|---|
| 182 | old = atomic_long_read(v: &ref->refcnt); | 
|---|
| 183 | if (likely(old == FILE_REF_ONEREF)) { | 
|---|
| 184 | if (likely(atomic_long_try_cmpxchg(&ref->refcnt, &old, FILE_REF_DEAD))) | 
|---|
| 185 | return true; | 
|---|
| 186 | } | 
|---|
| 187 | return file_ref_put(ref); | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | /** | 
|---|
| 191 | * file_ref_read - Read the number of file references | 
|---|
| 192 | * @ref: Pointer to the reference count | 
|---|
| 193 | * | 
|---|
| 194 | * Return: The number of held references (0 ... N) | 
|---|
| 195 | */ | 
|---|
| 196 | static inline unsigned long file_ref_read(file_ref_t *ref) | 
|---|
| 197 | { | 
|---|
| 198 | unsigned long c = atomic_long_read(v: &ref->refcnt); | 
|---|
| 199 |  | 
|---|
| 200 | /* Return 0 if within the DEAD zone. */ | 
|---|
| 201 | return c >= FILE_REF_RELEASED ? 0 : c + 1; | 
|---|
| 202 | } | 
|---|
| 203 |  | 
|---|
| 204 | /* | 
|---|
| 205 | * __file_ref_read_raw - Return the value stored in ref->refcnt | 
|---|
| 206 | * @ref: Pointer to the reference count | 
|---|
| 207 | * | 
|---|
| 208 | * Return: The raw value found in the counter | 
|---|
| 209 | * | 
|---|
| 210 | * A hack for file_needs_f_pos_lock(), you probably want to use | 
|---|
| 211 | * file_ref_read() instead. | 
|---|
| 212 | */ | 
|---|
| 213 | static inline unsigned long __file_ref_read_raw(file_ref_t *ref) | 
|---|
| 214 | { | 
|---|
| 215 | return atomic_long_read(v: &ref->refcnt); | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | #endif | 
|---|
| 219 |  | 
|---|