LCOV - code coverage report
Current view: top level - lib - refcount.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 9 39 23.1 %
Date: 2023-08-24 13:40:31 Functions: 2 6 33.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0
       2             : /*
       3             :  * Out-of-line refcount functions.
       4             :  */
       5             : 
       6             : #include <linux/mutex.h>
       7             : #include <linux/refcount.h>
       8             : #include <linux/spinlock.h>
       9             : #include <linux/bug.h>
      10             : 
      11             : #define REFCOUNT_WARN(str)      WARN_ONCE(1, "refcount_t: " str ".\n")
      12             : 
      13           0 : void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
      14             : {
      15           0 :         refcount_set(r, REFCOUNT_SATURATED);
      16             : 
      17           0 :         switch (t) {
      18             :         case REFCOUNT_ADD_NOT_ZERO_OVF:
      19           0 :                 REFCOUNT_WARN("saturated; leaking memory");
      20             :                 break;
      21             :         case REFCOUNT_ADD_OVF:
      22           0 :                 REFCOUNT_WARN("saturated; leaking memory");
      23             :                 break;
      24             :         case REFCOUNT_ADD_UAF:
      25           0 :                 REFCOUNT_WARN("addition on 0; use-after-free");
      26             :                 break;
      27             :         case REFCOUNT_SUB_UAF:
      28           0 :                 REFCOUNT_WARN("underflow; use-after-free");
      29             :                 break;
      30             :         case REFCOUNT_DEC_LEAK:
      31           0 :                 REFCOUNT_WARN("decrement hit 0; leaking memory");
      32             :                 break;
      33             :         default:
      34           0 :                 REFCOUNT_WARN("unknown saturation event!?");
      35             :         }
      36           0 : }
      37             : EXPORT_SYMBOL(refcount_warn_saturate);
      38             : 
      39             : /**
      40             :  * refcount_dec_if_one - decrement a refcount if it is 1
      41             :  * @r: the refcount
      42             :  *
      43             :  * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
      44             :  * success thereof.
      45             :  *
      46             :  * Like all decrement operations, it provides release memory order and provides
      47             :  * a control dependency.
      48             :  *
      49             :  * It can be used like a try-delete operator; this explicit case is provided
      50             :  * and not cmpxchg in generic, because that would allow implementing unsafe
      51             :  * operations.
      52             :  *
      53             :  * Return: true if the resulting refcount is 0, false otherwise
      54             :  */
      55           0 : bool refcount_dec_if_one(refcount_t *r)
      56             : {
      57           0 :         int val = 1;
      58             : 
      59           0 :         return atomic_try_cmpxchg_release(&r->refs, &val, 0);
      60             : }
      61             : EXPORT_SYMBOL(refcount_dec_if_one);
      62             : 
      63             : /**
      64             :  * refcount_dec_not_one - decrement a refcount if it is not 1
      65             :  * @r: the refcount
      66             :  *
      67             :  * No atomic_t counterpart, it decrements unless the value is 1, in which case
      68             :  * it will return false.
      69             :  *
      70             :  * Was often done like: atomic_add_unless(&var, -1, 1)
      71             :  *
      72             :  * Return: true if the decrement operation was successful, false otherwise
      73             :  */
      74         159 : bool refcount_dec_not_one(refcount_t *r)
      75             : {
      76         318 :         unsigned int new, val = atomic_read(&r->refs);
      77             : 
      78             :         do {
      79         159 :                 if (unlikely(val == REFCOUNT_SATURATED))
      80             :                         return true;
      81             : 
      82         159 :                 if (val == 1)
      83             :                         return false;
      84             : 
      85         159 :                 new = val - 1;
      86         159 :                 if (new > val) {
      87           0 :                         WARN_ONCE(new > val, "refcount_t: underflow; use-after-free.\n");
      88             :                         return true;
      89             :                 }
      90             : 
      91         318 :         } while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
      92             : 
      93             :         return true;
      94             : }
      95             : EXPORT_SYMBOL(refcount_dec_not_one);
      96             : 
      97             : /**
      98             :  * refcount_dec_and_mutex_lock - return holding mutex if able to decrement
      99             :  *                               refcount to 0
     100             :  * @r: the refcount
     101             :  * @lock: the mutex to be locked
     102             :  *
     103             :  * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
     104             :  * to decrement when saturated at REFCOUNT_SATURATED.
     105             :  *
     106             :  * Provides release memory ordering, such that prior loads and stores are done
     107             :  * before, and provides a control dependency such that free() must come after.
     108             :  * See the comment on top.
     109             :  *
     110             :  * Return: true and hold mutex if able to decrement refcount to 0, false
     111             :  *         otherwise
     112             :  */
     113           0 : bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
     114             : {
     115           0 :         if (refcount_dec_not_one(r))
     116             :                 return false;
     117             : 
     118           0 :         mutex_lock(lock);
     119           0 :         if (!refcount_dec_and_test(r)) {
     120           0 :                 mutex_unlock(lock);
     121           0 :                 return false;
     122             :         }
     123             : 
     124             :         return true;
     125             : }
     126             : EXPORT_SYMBOL(refcount_dec_and_mutex_lock);
     127             : 
     128             : /**
     129             :  * refcount_dec_and_lock - return holding spinlock if able to decrement
     130             :  *                         refcount to 0
     131             :  * @r: the refcount
     132             :  * @lock: the spinlock to be locked
     133             :  *
     134             :  * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
     135             :  * decrement when saturated at REFCOUNT_SATURATED.
     136             :  *
     137             :  * Provides release memory ordering, such that prior loads and stores are done
     138             :  * before, and provides a control dependency such that free() must come after.
     139             :  * See the comment on top.
     140             :  *
     141             :  * Return: true and hold spinlock if able to decrement refcount to 0, false
     142             :  *         otherwise
     143             :  */
     144           0 : bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
     145             : {
     146           0 :         if (refcount_dec_not_one(r))
     147             :                 return false;
     148             : 
     149           0 :         spin_lock(lock);
     150           0 :         if (!refcount_dec_and_test(r)) {
     151           0 :                 spin_unlock(lock);
     152           0 :                 return false;
     153             :         }
     154             : 
     155             :         return true;
     156             : }
     157             : EXPORT_SYMBOL(refcount_dec_and_lock);
     158             : 
     159             : /**
     160             :  * refcount_dec_and_lock_irqsave - return holding spinlock with disabled
     161             :  *                                 interrupts if able to decrement refcount to 0
     162             :  * @r: the refcount
     163             :  * @lock: the spinlock to be locked
     164             :  * @flags: saved IRQ-flags if the is acquired
     165             :  *
     166             :  * Same as refcount_dec_and_lock() above except that the spinlock is acquired
     167             :  * with disabled interrupts.
     168             :  *
     169             :  * Return: true and hold spinlock if able to decrement refcount to 0, false
     170             :  *         otherwise
     171             :  */
     172         159 : bool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock,
     173             :                                    unsigned long *flags)
     174             : {
     175         159 :         if (refcount_dec_not_one(r))
     176             :                 return false;
     177             : 
     178           0 :         spin_lock_irqsave(lock, *flags);
     179           0 :         if (!refcount_dec_and_test(r)) {
     180           0 :                 spin_unlock_irqrestore(lock, *flags);
     181           0 :                 return false;
     182             :         }
     183             : 
     184             :         return true;
     185             : }
     186             : EXPORT_SYMBOL(refcount_dec_and_lock_irqsave);

Generated by: LCOV version 1.14