Difference between revisions of "Kernel Protections/refcount t"
DavidWindsor (talk | contribs)  (Add Examples section)  | 
				|||
| (4 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
= Summary =    | = Summary =    | ||
The refcount_t API is a kernel self-protection mechanism that greatly helps with the prevention of [[Bug Classes/Use after free|use-after-free]] bugs.  It is based off of work done by the [https://pax.grsecurity.net PaX Team], originally called [https://forums.grsecurity.net/viewtopic.php?f=7&t=4173 PAX_REFCOUNT].  | |||
= Reference Counting API =  | = Reference Counting API =  | ||
Instead of the traditional <code>atomic_t</code>, reference counting uses a new data type: <code>refcount_t</code>.  This type is to be used for all kernel reference counters.     | |||
The following is the kernel reference counting API  | The following is the kernel reference counting API.  | ||
;'''<code>REFCOUNT_INIT(unsigned int)</code>'''  | ;'''<code>REFCOUNT_INIT(unsigned int)</code>'''  | ||
| Line 47: | Line 47: | ||
;'''<code>bool refcount_dec_and_lock(refcount_t *r, spinlock_t *s)</code>'''  | ;'''<code>bool refcount_dec_and_lock(refcount_t *r, spinlock_t *s)</code>'''  | ||
: Decrement <code>r</code> and lock spinlock if <code>r</code> becomes 0.  Will <code>WARN</code> on underflow and fail to decrement if <code>r</code> is saturated at <code>UINT_MAX</code>.  Returns <code>true</code> if <code>r</code> is 0 and spinlock is held, <code>false</code> otherwise.  | : Decrement <code>r</code> and lock spinlock if <code>r</code> becomes 0.  Will <code>WARN</code> on underflow and fail to decrement if <code>r</code> is saturated at <code>UINT_MAX</code>.  Returns <code>true</code> if <code>r</code> is 0 and spinlock is held, <code>false</code> otherwise.  | ||
= Examples =  | = Examples =  | ||
Latest revision as of 05:20, 4 August 2017
Summary
The refcount_t API is a kernel self-protection mechanism that greatly helps with the prevention of use-after-free bugs. It is based off of work done by the PaX Team, originally called PAX_REFCOUNT.
Reference Counting API
Instead of the traditional atomic_t, reference counting uses a new data type: refcount_t.  This type is to be used for all kernel reference counters.  
The following is the kernel reference counting API.
REFCOUNT_INIT(unsigned int)- Initialize a 
refcount_tobject. 
void refcount_set(refcount_t *, unsigned int)- Set a 
refcount_tobject's internal value. 
unsigned int refcount_read(refcount_t *)- Returns the 
refcount_tobject's internal value. 
bool refcount_add_not_zero(unsigned int v, refcount_t *r)- Add 
vtor. Ifr + vcauses an overflow, the result of the addition operation is not saved tor. Returnstrueif the resulting value ofris non-zero,falseotherwise. 
void refcount_add(unsigned int v, refcount_t *r)- Adds 
vtorand stores the value inr. 
bool refcount_inc_not_zero(refcount_t *r)- Increments 
rand tests whetherr + 1causes an overflow. If an overflow does occur, the result of the increment operation is not saved tor. Will saturate atUINT_MAXandWARN. Returnstrueif the resulting value ofris non-zero,falseotherwise. 
void refcount_inc(refcount_t *r)- Increment 
r. Will saturate atUINT_MAXandWARN. 
bool refcount_sub_and_test(unsigned int v, refcount_t *r)- Subtract 
vfromrand tests whetherr - vcauses an underflow. If an underflow does occur, the result of the decrement operation is not saved tor. Will fail to decrement when saturated atUINT_MAX. Returnstrueif the resulting value ofris non-zero,falseotherwise. 
void refcount_dec(refcount_t *r)- Decrement 
r. Ifr - 1causes an underflow, the result of the decrement operation is not saved tor. Will fail to decrement when saturated atUINT_MAX. 
bool refcount_dec_if_one(refcount_t *r)- Attempts to transition 
rfrom 1 to 0. Ifris 1, decrement it to 0. Returnstrueifrwas decremented,falseotherwise. 
bool refcount_dec_not_one(refcount_t *r)- Decrement 
runless the value ofris 1. Returnstrueifrwas decremented, false otherwise. 
bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)- Decrement 
rand lock mutex ifrbecomes 0. WillWARNon underflow and fail to decrement ifris saturated atUINT_MAX. Returnstrueifris 0 and mutex is held,falseotherwise. 
bool refcount_dec_and_lock(refcount_t *r, spinlock_t *s)- Decrement 
rand lock spinlock ifrbecomes 0. WillWARNon underflow and fail to decrement ifris saturated atUINT_MAX. Returnstrueifris 0 and spinlock is held,falseotherwise. 
Examples
The following use case is an instance of correct usage of the refcount_t API.  The object being counted is struct super_block, which represents a virtual filesystem superblock, an object containing a particular filesystem's metadata such as block size, the root inode, etc.  
Member Definition
This is the definition of the reference counter field in the struct super_block object.  If the object being counted is a structure, the reference counter is typically defined as a field of the counted structure, as we see in struct super_block below.
From include/linux/fs.h:
   struct super_block {
       ...
       refcount_t s_active;
       ...
   };
Object Initialization
When a counted object is created, its reference counter must be initialized to something sane, typically 1 (since, by virtue of being called in an "allocation" method, a user of the object already exists).
From fs/super.c:
   static struct super_block *alloc_super(struct file_system_type *type, int flags, struct user_namespace *user_ns)
   {
       struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);   
       ...
       refcount_set(&s->s_active, 1);
       ...
   }
Getting a New Reference
This code is executed when a user wishes to obtain a new reference to a struct super_block object.  The following code corresponds to the traditional reference counting "get" method.
From fs/super.c:
   static int grab_super(struct super_block *s) __releases(sb_lock)
   {
       s->s_count++;
       spin_unlock(&sb_lock);
       down_write(&s->s_umount);
       if ((s->s_flags & MS_BORN) && refcount_inc_not_zero(&s->s_active)) {
           put_super(s);
           return 1;
       }
       up_write(&s->s_umount);
       put_super(s);
       return 0;
   }
Releasing an Existing Reference
This code is executed when a user currently holding a reference to a struct super_block object no longer needs the object and wants to release it.  The following code corresponds to the traditional reference counting "put" method.  
From fs/super.c:
   void deactivate_locked_super(struct super_block *s) {
       ...
       if (refcount_dec_and_test(&s->s_active)) {
           ...
           put_super(s);
       }
   }
   void deactivate_super(struct super_block *s)
   {
       if (!refcount_dec_not_one(&s->s_active)) {
           down_write(&s->s_umount);nnNnnn
           deactivate_locked_super(s);
       }
   }