#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
+enum refcount_saturation_type {
+ REFCOUNT_ADD_NOT_ZERO_OVF,
+ REFCOUNT_ADD_OVF,
+ REFCOUNT_ADD_UAF,
+ REFCOUNT_SUB_UAF,
+ REFCOUNT_DEC_LEAK,
+};
+
+void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t);
+
/**
* refcount_set - set a refcount's value
* @r: the refcount
break;
} while (!atomic_try_cmpxchg_relaxed(&r->refs, &old, old + i));
- if (unlikely(old < 0 || old + i < 0)) {
- refcount_set(r, REFCOUNT_SATURATED);
- WARN_ONCE(1, "refcount_t: saturated; leaking memory.\n");
- }
+ if (unlikely(old < 0 || old + i < 0))
+ refcount_warn_saturate(r, REFCOUNT_ADD_NOT_ZERO_OVF);
return old;
}
{
int old = atomic_fetch_add_relaxed(i, &r->refs);
- WARN_ONCE(!old, "refcount_t: addition on 0; use-after-free.\n");
- if (unlikely(old <= 0 || old + i <= 0)) {
- refcount_set(r, REFCOUNT_SATURATED);
- WARN_ONCE(old, "refcount_t: saturated; leaking memory.\n");
- }
+ if (unlikely(!old))
+ refcount_warn_saturate(r, REFCOUNT_ADD_UAF);
+ else if (unlikely(old < 0 || old + i < 0))
+ refcount_warn_saturate(r, REFCOUNT_ADD_OVF);
}
/**
return true;
}
- if (unlikely(old < 0 || old - i < 0)) {
- refcount_set(r, REFCOUNT_SATURATED);
- WARN_ONCE(1, "refcount_t: underflow; use-after-free.\n");
- }
+ if (unlikely(old < 0 || old - i < 0))
+ refcount_warn_saturate(r, REFCOUNT_SUB_UAF);
return false;
}
*/
static inline void refcount_dec(refcount_t *r)
{
- int old = atomic_fetch_sub_release(1, &r->refs);
-
- if (unlikely(old <= 1)) {
- refcount_set(r, REFCOUNT_SATURATED);
- WARN_ONCE(1, "refcount_t: decrement hit 0; leaking memory.\n");
- }
+ if (unlikely(atomic_fetch_sub_release(1, &r->refs) <= 1))
+ refcount_warn_saturate(r, REFCOUNT_DEC_LEAK);
}
#else /* CONFIG_REFCOUNT_FULL */
#include <linux/spinlock.h>
#include <linux/bug.h>
+#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
+
+void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
+{
+ refcount_set(r, REFCOUNT_SATURATED);
+
+ switch (t) {
+ case REFCOUNT_ADD_NOT_ZERO_OVF:
+ REFCOUNT_WARN("saturated; leaking memory");
+ break;
+ case REFCOUNT_ADD_OVF:
+ REFCOUNT_WARN("saturated; leaking memory");
+ break;
+ case REFCOUNT_ADD_UAF:
+ REFCOUNT_WARN("addition on 0; use-after-free");
+ break;
+ case REFCOUNT_SUB_UAF:
+ REFCOUNT_WARN("underflow; use-after-free");
+ break;
+ case REFCOUNT_DEC_LEAK:
+ REFCOUNT_WARN("decrement hit 0; leaking memory");
+ break;
+ default:
+ REFCOUNT_WARN("unknown saturation event!?");
+ }
+}
+EXPORT_SYMBOL(refcount_warn_saturate);
+
/**
* refcount_dec_if_one - decrement a refcount if it is 1
* @r: the refcount