Difference between revisions of "Kernel Protections/refcount t"

From Linux Kernel Security Subsystem
Jump to navigation Jump to search
(Change <tt> tags to <code>)
(Add Examples section)
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 =
The following use case is an instance of correct usage of the <code>refcount_t</code> API.  The object being counted is <code>struct super_block</code>, 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 <code>struct super_block</code> 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 <code>struct super_block</code> below.
 
From <code>[http://lxr.free-electrons.com/source/include/linux/fs.h include/linux/fs.h]</code>:
<code>
    struct super_block {
        ...
        refcount_t s_active;
        ...
    };
</code>
==== 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 <code>[http://lxr.free-electrons.com/source/fs/super.c fs/super.c]</code>:
<code>
    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);
        ...
    }
</code>
==== Getting a New Reference ====
This code is executed when a user wishes to obtain a new reference to a <code>struct super_block</code> object.  The following code corresponds to the traditional reference counting "get" method.
From <code>[http://lxr.free-electrons.com/source/fs/super.c fs/super.c]</code>:
<code>
    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;
    }
</code>
==== Releasing an Existing Reference ====
This code is executed when a user currently holding a reference to a <code>struct super_block</code> object no longer needs the object and wants to release it.  The following code corresponds to the traditional reference counting "put" method. 
From <code>[http://lxr.free-electrons.com/source/fs/super.c fs/super.c]</code>:
<code>
    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);
        }
    }
</code>

Revision as of 14:45, 6 February 2017

Summary

HARDENED_ATOMIC is a kernel self-protection mechanism that greatly helps with the mitigation of use-after-free bugs. It is based off of work done by the PaX Team, originally called PAX_REFCOUNT.

Reference Counting API

HARDENED_ATOMIC introduces 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. Please note that all operations are atomic, unless otherwise specified.

REFCOUNT_INIT(unsigned int)
Initialize a refcount_t object.
void refcount_set(refcount_t *, unsigned int)
Set a refcount_t object's internal value.
unsigned int refcount_read(refcount_t *)
Returns the refcount_t object's internal value.
bool refcount_add_not_zero(unsigned int v, refcount_t *r)
Add v to r. If r + v causes an overflow, the result of the addition operation is not saved to r. Returns true if the resulting value of r is non-zero, false otherwise.
void refcount_add(unsigned int v, refcount_t *r)
Adds v to r and stores the value in r.
bool refcount_inc_not_zero(refcount_t *r)
Increments r and tests whether r + 1 causes an overflow. If an overflow does occur, the result of the increment operation is not saved to r. Will saturate at UINT_MAX and WARN. Returns true if the resulting value of r is non-zero, false otherwise.
void refcount_inc(refcount_t *r)
Increment r. Will saturate at UINT_MAX and WARN.
bool refcount_sub_and_test(unsigned int v, refcount_t *r)
Subtract v from r and tests whether r - v causes an underflow. If an underflow does occur, the result of the decrement operation is not saved to r. Will fail to decrement when saturated at UINT_MAX. Returns true if the resulting value of r is non-zero, false otherwise.
void refcount_dec(refcount_t *r)
Decrement r. If r - 1 causes an underflow, the result of the decrement operation is not saved to r. Will fail to decrement when saturated at UINT_MAX.
bool refcount_dec_if_one(refcount_t *r)
Attempts to transition r from 1 to 0. If r is 1, decrement it to 0. Returns true if r was decremented, false otherwise.
bool refcount_dec_not_one(refcount_t *r)
Decrement r unless the value of r is 1. Returns true if r was decremented, false otherwise.
bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
Decrement r and lock mutex if r becomes 0. Will WARN on underflow and fail to decrement if r is saturated at UINT_MAX. Returns true if r is 0 and mutex is held, false otherwise.
bool refcount_dec_and_lock(refcount_t *r, spinlock_t *s)
Decrement r and lock spinlock if r becomes 0. Will WARN on underflow and fail to decrement if r is saturated at UINT_MAX. Returns true if r is 0 and spinlock is held, false otherwise.


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);
       }
   }