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