at v6.18 6.9 kB view raw
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 46typedef 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 */ 59static inline void file_ref_init(file_ref_t *ref, unsigned long cnt) 60{ 61 atomic_long_set(&ref->refcnt, cnt - 1); 62} 63 64bool __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 */ 78static __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(1, &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 */ 118static __always_inline void file_ref_inc(file_ref_t *ref) 119{ 120 long prior = atomic_long_fetch_inc_relaxed(&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 */ 139static __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(&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 */ 178static __always_inline __must_check bool file_ref_put_close(file_ref_t *ref) 179{ 180 long old; 181 182 old = atomic_long_read(&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 */ 196static inline unsigned long file_ref_read(file_ref_t *ref) 197{ 198 unsigned long c = atomic_long_read(&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 */ 213static inline unsigned long __file_ref_read_raw(file_ref_t *ref) 214{ 215 return atomic_long_read(&ref->refcnt); 216} 217 218#endif