Difference between revisions of "Kernel Protections/refcount t"
m (KeesCook moved page Kernel Protections/HARDENED ATOMIC to Kernel Protections/refcount t) |
m (→Summary) |
||
| 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 = | ||
Revision as of 05:19, 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
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.
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);
}
}