security, keys: convert key.usage from atomic_t to refcount_t
refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova <elena.reshetova@intel.com> Signed-off-by: Hans Liljestrand <ishkamiel@gmail.com> Signed-off-by: Kees Cook <keescook@chromium.org> Signed-off-by: David Windsor <dwindsor@gmail.com> Acked-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <james.l.morris@oracle.com>
This commit is contained in:
parent
8291798dcf
commit
fff292914d
@ -23,6 +23,7 @@
|
|||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/assoc_array.h>
|
#include <linux/assoc_array.h>
|
||||||
|
#include <linux/refcount.h>
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
#include <linux/uidgid.h>
|
#include <linux/uidgid.h>
|
||||||
@ -135,7 +136,7 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
|
|||||||
* - Kerberos TGTs and tickets
|
* - Kerberos TGTs and tickets
|
||||||
*/
|
*/
|
||||||
struct key {
|
struct key {
|
||||||
atomic_t usage; /* number of references */
|
refcount_t usage; /* number of references */
|
||||||
key_serial_t serial; /* key serial number */
|
key_serial_t serial; /* key serial number */
|
||||||
union {
|
union {
|
||||||
struct list_head graveyard_link;
|
struct list_head graveyard_link;
|
||||||
@ -242,7 +243,7 @@ extern void key_put(struct key *key);
|
|||||||
|
|
||||||
static inline struct key *__key_get(struct key *key)
|
static inline struct key *__key_get(struct key *key)
|
||||||
{
|
{
|
||||||
atomic_inc(&key->usage);
|
refcount_inc(&key->usage);
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ continue_scanning:
|
|||||||
key = rb_entry(cursor, struct key, serial_node);
|
key = rb_entry(cursor, struct key, serial_node);
|
||||||
cursor = rb_next(cursor);
|
cursor = rb_next(cursor);
|
||||||
|
|
||||||
if (atomic_read(&key->usage) == 0)
|
if (refcount_read(&key->usage) == 0)
|
||||||
goto found_unreferenced_key;
|
goto found_unreferenced_key;
|
||||||
|
|
||||||
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
|
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) {
|
||||||
|
@ -285,7 +285,7 @@ struct key *key_alloc(struct key_type *type, const char *desc,
|
|||||||
if (!key->index_key.description)
|
if (!key->index_key.description)
|
||||||
goto no_memory_3;
|
goto no_memory_3;
|
||||||
|
|
||||||
atomic_set(&key->usage, 1);
|
refcount_set(&key->usage, 1);
|
||||||
init_rwsem(&key->sem);
|
init_rwsem(&key->sem);
|
||||||
lockdep_set_class(&key->sem, &type->lock_class);
|
lockdep_set_class(&key->sem, &type->lock_class);
|
||||||
key->index_key.type = type;
|
key->index_key.type = type;
|
||||||
@ -621,7 +621,7 @@ void key_put(struct key *key)
|
|||||||
if (key) {
|
if (key) {
|
||||||
key_check(key);
|
key_check(key);
|
||||||
|
|
||||||
if (atomic_dec_and_test(&key->usage))
|
if (refcount_dec_and_test(&key->usage))
|
||||||
schedule_work(&key_gc_work);
|
schedule_work(&key_gc_work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,7 +656,7 @@ not_found:
|
|||||||
|
|
||||||
found:
|
found:
|
||||||
/* pretend it doesn't exist if it is awaiting deletion */
|
/* pretend it doesn't exist if it is awaiting deletion */
|
||||||
if (atomic_read(&key->usage) == 0)
|
if (refcount_read(&key->usage) == 0)
|
||||||
goto not_found;
|
goto not_found;
|
||||||
|
|
||||||
/* this races with key_put(), but that doesn't matter since key_put()
|
/* this races with key_put(), but that doesn't matter since key_put()
|
||||||
|
@ -1033,7 +1033,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
|
|||||||
/* we've got a match but we might end up racing with
|
/* we've got a match but we might end up racing with
|
||||||
* key_cleanup() if the keyring is currently 'dead'
|
* key_cleanup() if the keyring is currently 'dead'
|
||||||
* (ie. it has a zero usage count) */
|
* (ie. it has a zero usage count) */
|
||||||
if (!atomic_inc_not_zero(&keyring->usage))
|
if (!refcount_inc_not_zero(&keyring->usage))
|
||||||
continue;
|
continue;
|
||||||
keyring->last_used_at = current_kernel_time().tv_sec;
|
keyring->last_used_at = current_kernel_time().tv_sec;
|
||||||
goto out;
|
goto out;
|
||||||
@ -1250,14 +1250,14 @@ int key_link(struct key *keyring, struct key *key)
|
|||||||
struct assoc_array_edit *edit;
|
struct assoc_array_edit *edit;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage));
|
kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage));
|
||||||
|
|
||||||
key_check(keyring);
|
key_check(keyring);
|
||||||
key_check(key);
|
key_check(key);
|
||||||
|
|
||||||
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
ret = __key_link_begin(keyring, &key->index_key, &edit);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));
|
kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage));
|
||||||
ret = __key_link_check_restriction(keyring, key);
|
ret = __key_link_check_restriction(keyring, key);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
ret = __key_link_check_live_key(keyring, key);
|
ret = __key_link_check_live_key(keyring, key);
|
||||||
@ -1266,7 +1266,7 @@ int key_link(struct key *keyring, struct key *key)
|
|||||||
__key_link_end(keyring, &key->index_key, edit);
|
__key_link_end(keyring, &key->index_key, edit);
|
||||||
}
|
}
|
||||||
|
|
||||||
kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));
|
kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(key_link);
|
EXPORT_SYMBOL(key_link);
|
||||||
|
@ -252,7 +252,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
|||||||
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
|
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
|
||||||
showflag(key, 'N', KEY_FLAG_NEGATIVE),
|
showflag(key, 'N', KEY_FLAG_NEGATIVE),
|
||||||
showflag(key, 'i', KEY_FLAG_INVALIDATED),
|
showflag(key, 'i', KEY_FLAG_INVALIDATED),
|
||||||
atomic_read(&key->usage),
|
refcount_read(&key->usage),
|
||||||
xbuf,
|
xbuf,
|
||||||
key->perm,
|
key->perm,
|
||||||
from_kuid_munged(seq_user_ns(m), key->uid),
|
from_kuid_munged(seq_user_ns(m), key->uid),
|
||||||
|
@ -213,7 +213,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error_inst;
|
goto error_inst;
|
||||||
|
|
||||||
kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage));
|
kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
|
||||||
return authkey;
|
return authkey;
|
||||||
|
|
||||||
auth_key_revoked:
|
auth_key_revoked:
|
||||||
|
Loading…
Reference in New Issue
Block a user