Difference between revisions of "Kernel Protections/refcount t"
DavidWindsor (talk | contribs) (Change <tt> tags to <code>) |
DavidWindsor (talk | contribs) (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
tor
. Ifr + v
causes an overflow, the result of the addition operation is not saved tor
. Returnstrue
if the resulting value ofr
is non-zero,false
otherwise.
void refcount_add(unsigned int v, refcount_t *r)
- Adds
v
tor
and stores the value inr
.
bool refcount_inc_not_zero(refcount_t *r)
- Increments
r
and tests whetherr + 1
causes an overflow. If an overflow does occur, the result of the increment operation is not saved tor
. Will saturate atUINT_MAX
andWARN
. Returnstrue
if the resulting value ofr
is non-zero,false
otherwise.
void refcount_inc(refcount_t *r)
- Increment
r
. Will saturate atUINT_MAX
andWARN
.
bool refcount_sub_and_test(unsigned int v, refcount_t *r)
- Subtract
v
fromr
and tests whetherr - v
causes 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
. Returnstrue
if the resulting value ofr
is non-zero,false
otherwise.
void refcount_dec(refcount_t *r)
- Decrement
r
. Ifr - 1
causes 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
r
from 1 to 0. Ifr
is 1, decrement it to 0. Returnstrue
ifr
was decremented,false
otherwise.
bool refcount_dec_not_one(refcount_t *r)
- Decrement
r
unless the value ofr
is 1. Returnstrue
ifr
was decremented, false otherwise.
bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
- Decrement
r
and lock mutex ifr
becomes 0. WillWARN
on underflow and fail to decrement ifr
is saturated atUINT_MAX
. Returnstrue
ifr
is 0 and mutex is held,false
otherwise.
bool refcount_dec_and_lock(refcount_t *r, spinlock_t *s)
- Decrement
r
and lock spinlock ifr
becomes 0. WillWARN
on underflow and fail to decrement ifr
is saturated atUINT_MAX
. Returnstrue
ifr
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);
}
}