xref: /linux-6.15/include/linux/file_ref.h (revision d1f7256a)
108ef26eaSChristian Brauner /* SPDX-License-Identifier: GPL-2.0-only */
208ef26eaSChristian Brauner #ifndef _LINUX_FILE_REF_H
308ef26eaSChristian Brauner #define _LINUX_FILE_REF_H
408ef26eaSChristian Brauner 
508ef26eaSChristian Brauner #include <linux/atomic.h>
608ef26eaSChristian Brauner #include <linux/preempt.h>
708ef26eaSChristian Brauner #include <linux/types.h>
808ef26eaSChristian Brauner 
908ef26eaSChristian Brauner /*
1008ef26eaSChristian Brauner  * file_ref is a reference count implementation specifically for use by
1108ef26eaSChristian Brauner  * files. It takes inspiration from rcuref but differs in key aspects
1208ef26eaSChristian Brauner  * such as support for SLAB_TYPESAFE_BY_RCU type caches.
1308ef26eaSChristian Brauner  *
1408ef26eaSChristian Brauner  * FILE_REF_ONEREF                FILE_REF_MAXREF
1508ef26eaSChristian Brauner  * 0x0000000000000000UL      0x7FFFFFFFFFFFFFFFUL
1608ef26eaSChristian Brauner  * <-------------------valid ------------------->
1708ef26eaSChristian Brauner  *
1808ef26eaSChristian Brauner  *                       FILE_REF_SATURATED
1908ef26eaSChristian Brauner  * 0x8000000000000000UL 0xA000000000000000UL 0xBFFFFFFFFFFFFFFFUL
2008ef26eaSChristian Brauner  * <-----------------------saturation zone---------------------->
2108ef26eaSChristian Brauner  *
2208ef26eaSChristian Brauner  * FILE_REF_RELEASED                   FILE_REF_DEAD
2308ef26eaSChristian Brauner  * 0xC000000000000000UL         0xE000000000000000UL
2408ef26eaSChristian Brauner  * <-------------------dead zone------------------->
2508ef26eaSChristian Brauner  *
2608ef26eaSChristian Brauner  * FILE_REF_NOREF
2708ef26eaSChristian Brauner  * 0xFFFFFFFFFFFFFFFFUL
2808ef26eaSChristian Brauner  */
2908ef26eaSChristian Brauner 
3008ef26eaSChristian Brauner #ifdef CONFIG_64BIT
3108ef26eaSChristian Brauner #define FILE_REF_ONEREF		0x0000000000000000UL
3208ef26eaSChristian Brauner #define FILE_REF_MAXREF		0x7FFFFFFFFFFFFFFFUL
3308ef26eaSChristian Brauner #define FILE_REF_SATURATED	0xA000000000000000UL
3408ef26eaSChristian Brauner #define FILE_REF_RELEASED	0xC000000000000000UL
3508ef26eaSChristian Brauner #define FILE_REF_DEAD		0xE000000000000000UL
3608ef26eaSChristian Brauner #define FILE_REF_NOREF		0xFFFFFFFFFFFFFFFFUL
3708ef26eaSChristian Brauner #else
3808ef26eaSChristian Brauner #define FILE_REF_ONEREF		0x00000000U
3908ef26eaSChristian Brauner #define FILE_REF_MAXREF		0x7FFFFFFFU
4008ef26eaSChristian Brauner #define FILE_REF_SATURATED	0xA0000000U
4108ef26eaSChristian Brauner #define FILE_REF_RELEASED	0xC0000000U
4208ef26eaSChristian Brauner #define FILE_REF_DEAD		0xE0000000U
4308ef26eaSChristian Brauner #define FILE_REF_NOREF		0xFFFFFFFFU
4408ef26eaSChristian Brauner #endif
4508ef26eaSChristian Brauner 
4608ef26eaSChristian Brauner typedef struct {
4708ef26eaSChristian Brauner #ifdef CONFIG_64BIT
4808ef26eaSChristian Brauner 	atomic64_t refcnt;
4908ef26eaSChristian Brauner #else
5008ef26eaSChristian Brauner 	atomic_t refcnt;
5108ef26eaSChristian Brauner #endif
5208ef26eaSChristian Brauner } file_ref_t;
5308ef26eaSChristian Brauner 
5408ef26eaSChristian Brauner /**
5508ef26eaSChristian Brauner  * file_ref_init - Initialize a file reference count
5608ef26eaSChristian Brauner  * @ref: Pointer to the reference count
5708ef26eaSChristian Brauner  * @cnt: The initial reference count typically '1'
5808ef26eaSChristian Brauner  */
file_ref_init(file_ref_t * ref,unsigned long cnt)5908ef26eaSChristian Brauner static inline void file_ref_init(file_ref_t *ref, unsigned long cnt)
6008ef26eaSChristian Brauner {
6108ef26eaSChristian Brauner 	atomic_long_set(&ref->refcnt, cnt - 1);
6208ef26eaSChristian Brauner }
6308ef26eaSChristian Brauner 
6408ef26eaSChristian Brauner bool __file_ref_put(file_ref_t *ref, unsigned long cnt);
6508ef26eaSChristian Brauner 
6608ef26eaSChristian Brauner /**
6708ef26eaSChristian Brauner  * file_ref_get - Acquire one reference on a file
6808ef26eaSChristian Brauner  * @ref: Pointer to the reference count
6908ef26eaSChristian Brauner  *
7008ef26eaSChristian Brauner  * Similar to atomic_inc_not_zero() but saturates at FILE_REF_MAXREF.
7108ef26eaSChristian Brauner  *
7208ef26eaSChristian Brauner  * Provides full memory ordering.
7308ef26eaSChristian Brauner  *
7408ef26eaSChristian Brauner  * Return: False if the attempt to acquire a reference failed. This happens
7508ef26eaSChristian Brauner  *         when the last reference has been put already. True if a reference
7608ef26eaSChristian Brauner  *         was successfully acquired
7708ef26eaSChristian Brauner  */
file_ref_get(file_ref_t * ref)7808ef26eaSChristian Brauner static __always_inline __must_check bool file_ref_get(file_ref_t *ref)
7908ef26eaSChristian Brauner {
8008ef26eaSChristian Brauner 	/*
8108ef26eaSChristian Brauner 	 * Unconditionally increase the reference count with full
8208ef26eaSChristian Brauner 	 * ordering. The saturation and dead zones provide enough
8308ef26eaSChristian Brauner 	 * tolerance for this.
8408ef26eaSChristian Brauner 	 *
8508ef26eaSChristian Brauner 	 * If this indicates negative the file in question the fail can
8608ef26eaSChristian Brauner 	 * be freed and immediately reused due to SLAB_TYPSAFE_BY_RCU.
8708ef26eaSChristian Brauner 	 * Hence, unconditionally altering the file reference count to
8808ef26eaSChristian Brauner 	 * e.g., reset the file reference count back to the middle of
8908ef26eaSChristian Brauner 	 * the deadzone risk end up marking someone else's file as dead
9008ef26eaSChristian Brauner 	 * behind their back.
9108ef26eaSChristian Brauner 	 *
9208ef26eaSChristian Brauner 	 * It would be possible to do a careful:
9308ef26eaSChristian Brauner 	 *
9408ef26eaSChristian Brauner 	 * cnt = atomic_long_inc_return();
9508ef26eaSChristian Brauner 	 * if (likely(cnt >= 0))
9608ef26eaSChristian Brauner 	 *	return true;
9708ef26eaSChristian Brauner 	 *
9808ef26eaSChristian Brauner 	 * and then something like:
9908ef26eaSChristian Brauner 	 *
10008ef26eaSChristian Brauner 	 * if (cnt >= FILE_REF_RELEASE)
10108ef26eaSChristian Brauner 	 *	atomic_long_try_cmpxchg(&ref->refcnt, &cnt, FILE_REF_DEAD),
10208ef26eaSChristian Brauner 	 *
10308ef26eaSChristian Brauner 	 * to set the value back to the middle of the deadzone. But it's
10408ef26eaSChristian Brauner 	 * practically impossible to go from FILE_REF_DEAD to
10508ef26eaSChristian Brauner 	 * FILE_REF_ONEREF. It would need 2305843009213693952/2^61
10608ef26eaSChristian Brauner 	 * file_ref_get()s to resurrect such a dead file.
10708ef26eaSChristian Brauner 	 */
10808ef26eaSChristian Brauner 	return !atomic_long_add_negative(1, &ref->refcnt);
10908ef26eaSChristian Brauner }
11008ef26eaSChristian Brauner 
11108ef26eaSChristian Brauner /**
11208ef26eaSChristian Brauner  * file_ref_inc - Acquire one reference on a file
11308ef26eaSChristian Brauner  * @ref: Pointer to the reference count
11408ef26eaSChristian Brauner  *
11508ef26eaSChristian Brauner  * Acquire an additional reference on a file. Warns if the caller didn't
11608ef26eaSChristian Brauner  * already hold a reference.
11708ef26eaSChristian Brauner  */
file_ref_inc(file_ref_t * ref)11808ef26eaSChristian Brauner static __always_inline void file_ref_inc(file_ref_t *ref)
11908ef26eaSChristian Brauner {
12008ef26eaSChristian Brauner 	long prior = atomic_long_fetch_inc_relaxed(&ref->refcnt);
12108ef26eaSChristian Brauner 	WARN_ONCE(prior < 0, "file_ref_inc() on a released file reference");
12208ef26eaSChristian Brauner }
12308ef26eaSChristian Brauner 
12408ef26eaSChristian Brauner /**
12508ef26eaSChristian Brauner  * file_ref_put -- Release a file reference
12608ef26eaSChristian Brauner  * @ref:	Pointer to the reference count
12708ef26eaSChristian Brauner  *
12808ef26eaSChristian Brauner  * Provides release memory ordering, such that prior loads and stores
12908ef26eaSChristian Brauner  * are done before, and provides an acquire ordering on success such
13008ef26eaSChristian Brauner  * that free() must come after.
13108ef26eaSChristian Brauner  *
13208ef26eaSChristian Brauner  * Return: True if this was the last reference with no future references
13308ef26eaSChristian Brauner  *         possible. This signals the caller that it can safely release
13408ef26eaSChristian Brauner  *         the object which is protected by the reference counter.
13508ef26eaSChristian Brauner  *         False if there are still active references or the put() raced
13608ef26eaSChristian Brauner  *         with a concurrent get()/put() pair. Caller is not allowed to
13708ef26eaSChristian Brauner  *         release the protected object.
13808ef26eaSChristian Brauner  */
file_ref_put(file_ref_t * ref)13908ef26eaSChristian Brauner static __always_inline __must_check bool file_ref_put(file_ref_t *ref)
14008ef26eaSChristian Brauner {
14108ef26eaSChristian Brauner 	long cnt;
14208ef26eaSChristian Brauner 
14308ef26eaSChristian Brauner 	/*
14408ef26eaSChristian Brauner 	 * While files are SLAB_TYPESAFE_BY_RCU and thus file_ref_put()
14508ef26eaSChristian Brauner 	 * calls don't risk UAFs when a file is recyclyed, it is still
14608ef26eaSChristian Brauner 	 * vulnerable to UAFs caused by freeing the whole slab page once
14708ef26eaSChristian Brauner 	 * it becomes unused. Prevent file_ref_put() from being
14808ef26eaSChristian Brauner 	 * preempted protects against this.
14908ef26eaSChristian Brauner 	 */
15008ef26eaSChristian Brauner 	guard(preempt)();
15108ef26eaSChristian Brauner 	/*
15208ef26eaSChristian Brauner 	 * Unconditionally decrease the reference count. The saturation
15308ef26eaSChristian Brauner 	 * and dead zones provide enough tolerance for this. If this
15408ef26eaSChristian Brauner 	 * fails then we need to handle the last reference drop and
15508ef26eaSChristian Brauner 	 * cases inside the saturation and dead zones.
15608ef26eaSChristian Brauner 	 */
15708ef26eaSChristian Brauner 	cnt = atomic_long_dec_return(&ref->refcnt);
15808ef26eaSChristian Brauner 	if (cnt >= 0)
15908ef26eaSChristian Brauner 		return false;
16008ef26eaSChristian Brauner 	return __file_ref_put(ref, cnt);
16108ef26eaSChristian Brauner }
16208ef26eaSChristian Brauner 
16308ef26eaSChristian Brauner /**
164e8358845SMateusz Guzik  * file_ref_put_close - drop a reference expecting it would transition to FILE_REF_NOREF
165e8358845SMateusz Guzik  * @ref:	Pointer to the reference count
166e8358845SMateusz Guzik  *
167e8358845SMateusz Guzik  * Semantically it is equivalent to calling file_ref_put(), but it trades lower
168e8358845SMateusz Guzik  * performance in face of other CPUs also modifying the refcount for higher
169e8358845SMateusz Guzik  * performance when this happens to be the last reference.
170e8358845SMateusz Guzik  *
171e8358845SMateusz Guzik  * For the last reference file_ref_put() issues 2 atomics. One to drop the
172e8358845SMateusz Guzik  * reference and another to transition it to FILE_REF_DEAD. This routine does
173e8358845SMateusz Guzik  * the work in one step, but in order to do it has to pre-read the variable which
174e8358845SMateusz Guzik  * decreases scalability.
175e8358845SMateusz Guzik  *
176e8358845SMateusz Guzik  * Use with close() et al, stick to file_ref_put() by default.
177e8358845SMateusz Guzik  */
file_ref_put_close(file_ref_t * ref)178e8358845SMateusz Guzik static __always_inline __must_check bool file_ref_put_close(file_ref_t *ref)
179e8358845SMateusz Guzik {
180*d1f7256aSMateusz Guzik 	long old;
181e8358845SMateusz Guzik 
182e8358845SMateusz Guzik 	old = atomic_long_read(&ref->refcnt);
183*d1f7256aSMateusz Guzik 	if (likely(old == FILE_REF_ONEREF)) {
184*d1f7256aSMateusz Guzik 		if (likely(atomic_long_try_cmpxchg(&ref->refcnt, &old, FILE_REF_DEAD)))
185*d1f7256aSMateusz Guzik 			return true;
186*d1f7256aSMateusz Guzik 	}
187*d1f7256aSMateusz Guzik 	return file_ref_put(ref);
188e8358845SMateusz Guzik }
189e8358845SMateusz Guzik 
190e8358845SMateusz Guzik /**
19108ef26eaSChristian Brauner  * file_ref_read - Read the number of file references
19208ef26eaSChristian Brauner  * @ref: Pointer to the reference count
19308ef26eaSChristian Brauner  *
19408ef26eaSChristian Brauner  * Return: The number of held references (0 ... N)
19508ef26eaSChristian Brauner  */
file_ref_read(file_ref_t * ref)19608ef26eaSChristian Brauner static inline unsigned long file_ref_read(file_ref_t *ref)
19708ef26eaSChristian Brauner {
19808ef26eaSChristian Brauner 	unsigned long c = atomic_long_read(&ref->refcnt);
19908ef26eaSChristian Brauner 
20008ef26eaSChristian Brauner 	/* Return 0 if within the DEAD zone. */
20108ef26eaSChristian Brauner 	return c >= FILE_REF_RELEASED ? 0 : c + 1;
20208ef26eaSChristian Brauner }
20308ef26eaSChristian Brauner 
2045370b43eSMateusz Guzik /*
2055370b43eSMateusz Guzik  * __file_ref_read_raw - Return the value stored in ref->refcnt
2065370b43eSMateusz Guzik  * @ref: Pointer to the reference count
2075370b43eSMateusz Guzik  *
2085370b43eSMateusz Guzik  * Return: The raw value found in the counter
2095370b43eSMateusz Guzik  *
2105370b43eSMateusz Guzik  * A hack for file_needs_f_pos_lock(), you probably want to use
2115370b43eSMateusz Guzik  * file_ref_read() instead.
2125370b43eSMateusz Guzik  */
__file_ref_read_raw(file_ref_t * ref)2135370b43eSMateusz Guzik static inline unsigned long __file_ref_read_raw(file_ref_t *ref)
2145370b43eSMateusz Guzik {
2155370b43eSMateusz Guzik 	return atomic_long_read(&ref->refcnt);
2165370b43eSMateusz Guzik }
2175370b43eSMateusz Guzik 
21808ef26eaSChristian Brauner #endif
219