diff options
author | Miloslav Trmač <mitr@redhat.com> | 2010-07-31 00:51:46 +0200 |
---|---|---|
committer | Miloslav Trmač <mitr@redhat.com> | 2010-07-31 00:51:46 +0200 |
commit | 49d7012c4c62f6681378a56d9af4d721178b8ad3 (patch) | |
tree | f628266a0c07d747dea77fca8cc08a6ff30b912b /crypto | |
parent | dbff0a8826b2dd9abc0c7699ae883a95d468b12c (diff) | |
parent | 3f11cfa0d94fcdc42db8812118e7386050660d6e (diff) | |
download | kernel-crypto-49d7012c4c62f6681378a56d9af4d721178b8ad3.tar.gz kernel-crypto-49d7012c4c62f6681378a56d9af4d721178b8ad3.tar.xz kernel-crypto-49d7012c4c62f6681378a56d9af4d721178b8ad3.zip |
Merge branch 'standalone-master' into standalone-rename
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/userspace/ncr-int.h | 61 | ||||
-rw-r--r-- | crypto/userspace/ncr-key-wrap.c | 22 | ||||
-rw-r--r-- | crypto/userspace/ncr-key.c | 200 | ||||
-rw-r--r-- | crypto/userspace/ncr-limits.c | 175 | ||||
-rw-r--r-- | crypto/userspace/ncr-pk.c | 8 | ||||
-rw-r--r-- | crypto/userspace/ncr-sessions.c | 129 | ||||
-rw-r--r-- | crypto/userspace/ncr.c | 36 |
7 files changed, 328 insertions, 303 deletions
diff --git a/crypto/userspace/ncr-int.h b/crypto/userspace/ncr-int.h index e79747c12ca..008a89b181f 100644 --- a/crypto/userspace/ncr-int.h +++ b/crypto/userspace/ncr-int.h @@ -1,6 +1,8 @@ #ifndef NCR_INT_H # define NCR_INT_H +#include <linux/idr.h> +#include <linux/mutex.h> #include "ncr.h" #include <asm/atomic.h> #include "cryptodev_int.h" @@ -31,8 +33,6 @@ struct algo_properties_st { }; struct session_item_st { - struct list_head list; - const struct algo_properties_st *algorithm; ncr_crypto_op_t op; @@ -59,7 +59,6 @@ struct session_item_st { }; struct key_item_st { - struct list_head list; /* This object is also not protected from concurrent access. */ ncr_key_type_t type; @@ -90,19 +89,16 @@ struct key_item_st { ncr_key_t desc; }; -struct list_sem_st { - struct list_head list; - struct semaphore sem; -}; - /* all the data associated with the open descriptor * are here. */ struct ncr_lists { - struct list_sem_st key; + struct mutex key_idr_mutex; + struct idr key_idr; /* sessions */ - struct list_sem_st sessions; + struct mutex session_idr_mutex; + struct idr session_idr; }; void* ncr_init_lists(void); @@ -112,29 +108,30 @@ int ncr_ioctl(struct ncr_lists*, struct file *filp, unsigned int cmd, unsigned long arg); /* key derivation */ -int ncr_key_derive(struct list_sem_st* key_lst, void __user* arg); +int ncr_key_derive(struct ncr_lists *lst, void __user* arg); /* key handling */ -int ncr_key_init(struct list_sem_st*, void __user* arg); -int ncr_key_deinit(struct list_sem_st*, void __user* arg); -int ncr_key_export(struct list_sem_st* key_lst,void __user* arg); -int ncr_key_import(struct list_sem_st* key_lst,void __user* arg); -void ncr_key_list_deinit(struct list_sem_st* lst); -int ncr_key_generate(struct list_sem_st* data_lst, void __user* arg); -int ncr_key_info(struct list_sem_st*, void __user* arg); - -int ncr_key_generate_pair(struct list_sem_st* lst, void __user* arg); -int ncr_key_get_public(struct list_sem_st* lst, void __user* arg); - -int ncr_key_item_get_read(struct key_item_st**st, struct list_sem_st* lst, +int ncr_key_init(struct ncr_lists *lst, void __user* arg); +int ncr_key_deinit(struct ncr_lists *lst, void __user* arg); +int ncr_key_export(struct ncr_lists *lst, void __user* arg); +int ncr_key_import(struct ncr_lists *lst, void __user* arg); +void ncr_key_list_deinit(struct ncr_lists *lst); +int ncr_key_generate(struct ncr_lists *lst, void __user* arg); +int ncr_key_info(struct ncr_lists *lst, void __user* arg); + +int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg); +int ncr_key_get_public(struct ncr_lists *lst, void __user* arg); + +int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst, ncr_key_t desc); /* get key item for writing */ -int ncr_key_item_get_write( struct key_item_st** st, - struct list_sem_st* lst, ncr_key_t desc); +int ncr_key_item_get_write( struct key_item_st** st, + struct ncr_lists *lst, ncr_key_t desc); void _ncr_key_item_put( struct key_item_st* item); typedef enum { LIMIT_TYPE_KEY, + NUM_LIMIT_TYPES } limits_type_t; void ncr_limits_remove(uid_t uid, pid_t pid, limits_type_t type); @@ -142,16 +139,16 @@ int ncr_limits_add_and_check(uid_t uid, pid_t pid, limits_type_t type); void ncr_limits_init(void); void ncr_limits_deinit(void); -int ncr_key_wrap(struct list_sem_st* keys, void __user* arg); -int ncr_key_unwrap(struct list_sem_st*, void __user* arg); -int ncr_key_storage_wrap(struct list_sem_st* key_lst, void __user* arg); -int ncr_key_storage_unwrap(struct list_sem_st*, void __user* arg); +int ncr_key_wrap(struct ncr_lists *lst, void __user* arg); +int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg); +int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg); +int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg); /* sessions */ -struct session_item_st* ncr_session_new(struct list_sem_st* lst); +struct session_item_st* ncr_session_new(struct ncr_lists *lst); void _ncr_sessions_item_put( struct session_item_st* item); -struct session_item_st* ncr_sessions_item_get( struct list_sem_st* lst, ncr_session_t desc); -void ncr_sessions_list_deinit(struct list_sem_st* lst); +struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc); +void ncr_sessions_list_deinit(struct ncr_lists *lst); int ncr_session_init(struct ncr_lists* lists, void __user* arg); int ncr_session_update(struct ncr_lists* lists, void __user* arg); diff --git a/crypto/userspace/ncr-key-wrap.c b/crypto/userspace/ncr-key-wrap.c index cb431c9b5aa..34d8c527b83 100644 --- a/crypto/userspace/ncr-key-wrap.c +++ b/crypto/userspace/ncr-key-wrap.c @@ -403,7 +403,7 @@ val64_t * R = NULL; ret = rfc3394_unwrap(wrapped_key, R, n, A, &ctx); if (ret < 0) { err(); - return ret; + goto cleanup; } if (memcmp(A, iv, 8)!= 0) { @@ -429,7 +429,7 @@ cleanup: return ret; } -int ncr_key_wrap(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_wrap(struct ncr_lists *lst, void __user* arg) { struct ncr_key_wrap_st wrap; struct key_item_st* wkey = NULL; @@ -443,7 +443,7 @@ int ret; return -EFAULT; } - ret = ncr_key_item_get_read( &wkey, key_lst, wrap.keytowrap); + ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap); if (ret < 0) { err(); return ret; @@ -455,7 +455,7 @@ int ret; goto fail; } - ret = ncr_key_item_get_read( &key, key_lst, wrap.key); + ret = ncr_key_item_get_read( &key, lst, wrap.key); if (ret < 0) { err(); goto fail; @@ -512,7 +512,7 @@ fail: /* Unwraps keys. All keys unwrapped are not accessible by * userspace. */ -int ncr_key_unwrap(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_unwrap(struct ncr_lists *lst, void __user* arg) { struct ncr_key_wrap_st wrap; struct key_item_st* wkey = NULL; @@ -526,13 +526,13 @@ int ret; return -EFAULT; } - ret = ncr_key_item_get_write( &wkey, key_lst, wrap.keytowrap); + ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap); if (ret < 0) { err(); return ret; } - ret = ncr_key_item_get_read( &key, key_lst, wrap.key); + ret = ncr_key_item_get_read( &key, lst, wrap.key); if (ret < 0) { err(); goto fail; @@ -574,7 +574,7 @@ fail: return ret; } -int ncr_key_storage_wrap(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_storage_wrap(struct ncr_lists *lst, void __user* arg) { struct ncr_key_storage_wrap_st wrap; struct key_item_st* wkey = NULL; @@ -594,7 +594,7 @@ int ret; return -EFAULT; } - ret = ncr_key_item_get_read( &wkey, key_lst, wrap.keytowrap); + ret = ncr_key_item_get_read( &wkey, lst, wrap.keytowrap); if (ret < 0) { err(); return ret; @@ -650,7 +650,7 @@ fail: /* Unwraps keys. All keys unwrapped are not accessible by * userspace. */ -int ncr_key_storage_unwrap(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_storage_unwrap(struct ncr_lists *lst, void __user* arg) { struct ncr_key_storage_wrap_st wrap; struct key_item_st* wkey = NULL; @@ -669,7 +669,7 @@ int ret; return -EFAULT; } - ret = ncr_key_item_get_write( &wkey, key_lst, wrap.keytowrap); + ret = ncr_key_item_get_write( &wkey, lst, wrap.keytowrap); if (ret < 0) { err(); return ret; diff --git a/crypto/userspace/ncr-key.c b/crypto/userspace/ncr-key.c index 4ea933cd591..d4f32b1ef85 100644 --- a/crypto/userspace/ncr-key.c +++ b/crypto/userspace/ncr-key.c @@ -33,44 +33,26 @@ static void ncr_key_clear(struct key_item_st* item); -/* must be called with data semaphore down */ -static void _ncr_key_unlink_item(struct key_item_st *item) +static int key_list_deinit_fn(int id, void *item, void *unused) { - list_del(&item->list); - _ncr_key_item_put( item); /* decrement ref count */ -} - -void ncr_key_list_deinit(struct list_sem_st* lst) -{ - if(lst) { - struct key_item_st * item, *tmp; - - down(&lst->sem); - - list_for_each_entry_safe(item, tmp, &lst->list, list) { - _ncr_key_unlink_item(item); - } - up(&lst->sem); - } + (void)unused; + _ncr_key_item_put(item); + return 0; } -/* must be called with data semaphore down - */ -static ncr_key_t _ncr_key_get_new_desc( struct list_sem_st* lst) +void ncr_key_list_deinit(struct ncr_lists *lst) { -struct key_item_st* item; -int mx = 1; - - list_for_each_entry(item, &lst->list, list) { - mx = max(mx, item->desc); - } - mx++; - - return mx; + /* The mutex is not necessary, but doesn't hurt and makes it easier to + verify locking correctness. */ + mutex_lock(&lst->key_idr_mutex); + idr_for_each(&lst->key_idr, key_list_deinit_fn, NULL); + idr_remove_all(&lst->key_idr); + idr_destroy(&lst->key_idr); + mutex_unlock(&lst->key_idr_mutex); } /* returns the data item corresponding to desc */ -int ncr_key_item_get_read(struct key_item_st**st, struct list_sem_st* lst, +int ncr_key_item_get_read(struct key_item_st**st, struct ncr_lists *lst, ncr_key_t desc) { struct key_item_st* item; @@ -78,28 +60,27 @@ int ret; *st = NULL; - down(&lst->sem); - list_for_each_entry(item, &lst->list, list) { - if (item->desc == desc) { - atomic_inc(&item->refcnt); - - if (atomic_read(&item->writer) != 0) { - /* writer in place busy */ - atomic_dec(&item->refcnt); - ret = -EBUSY; - goto exit; - } - - *st = item; - ret = 0; - goto exit; - } + mutex_lock(&lst->key_idr_mutex); + item = idr_find(&lst->key_idr, desc); + if (item == NULL) { + err(); + ret = -EINVAL; + goto exit; } + atomic_inc(&item->refcnt); + + if (atomic_read(&item->writer) != 0) { + /* writer in place busy */ + atomic_dec(&item->refcnt); + ret = -EBUSY; + goto exit; + } + + *st = item; + ret = 0; - err(); - ret = -EINVAL; exit: - up(&lst->sem); + mutex_unlock(&lst->key_idr_mutex); return ret; } @@ -107,42 +88,40 @@ exit: * is in use. */ int ncr_key_item_get_write( struct key_item_st** st, - struct list_sem_st* lst, ncr_key_t desc) + struct ncr_lists *lst, ncr_key_t desc) { struct key_item_st* item; int ret; *st = NULL; - down(&lst->sem); - list_for_each_entry(item, &lst->list, list) { - if (item->desc == desc) { - /* do not return items that are in use already */ + mutex_lock(&lst->key_idr_mutex); + item = idr_find(&lst->key_idr, desc); + if (item == NULL) { + err(); + ret = -EINVAL; + goto exit; + } + /* do not return items that are in use already */ - if (atomic_add_unless(&item->writer, 1, 1)==0) { - /* another writer so busy */ - ret = -EBUSY; - goto exit; - } - - if (atomic_add_unless(&item->refcnt, 1, 2)==0) { - /* some reader is active so busy */ - atomic_dec(&item->writer); - ret = -EBUSY; - goto exit; - } + if (atomic_add_unless(&item->writer, 1, 1)==0) { + /* another writer so busy */ + ret = -EBUSY; + goto exit; + } - *st = item; - ret = 0; - goto exit; - } + if (atomic_add_unless(&item->refcnt, 1, 2)==0) { + /* some reader is active so busy */ + atomic_dec(&item->writer); + ret = -EBUSY; + goto exit; } - err(); - ret = -EINVAL; + *st = item; + ret = 0; exit: - up(&lst->sem); + mutex_unlock(&lst->key_idr_mutex); return ret; } @@ -157,7 +136,21 @@ void _ncr_key_item_put( struct key_item_st* item) } } -int ncr_key_init(struct list_sem_st* lst, void __user* arg) +static void _ncr_key_remove(struct ncr_lists *lst, ncr_key_t desc) +{ + struct key_item_st * item; + + mutex_lock(&lst->key_idr_mutex); + item = idr_find(&lst->key_idr, desc); + if (item != NULL) + idr_remove(&lst->key_idr, desc); /* Steal the reference */ + mutex_unlock(&lst->key_idr_mutex); + + if (item != NULL) + _ncr_key_item_put(item); +} + +int ncr_key_init(struct ncr_lists *lst, void __user* arg) { ncr_key_t desc; struct key_item_st* key; @@ -180,23 +173,25 @@ int ncr_key_init(struct list_sem_st* lst, void __user* arg) atomic_set(&key->refcnt, 1); atomic_set(&key->writer, 0); - - down(&lst->sem); - - key->desc = _ncr_key_get_new_desc(lst); key->uid = current_euid(); key->pid = task_pid_nr(current); - list_add(&key->list, &lst->list); - + mutex_lock(&lst->key_idr_mutex); + /* idr_pre_get() should preallocate enough, and, due to key_idr_mutex, + nobody else can use the preallocated data. Therefore the loop + recommended in idr_get_new() documentation is not necessary. */ + if (idr_pre_get(&lst->key_idr, GFP_KERNEL) == 0 || + idr_get_new(&lst->key_idr, key, &key->desc) != 0) { + mutex_unlock(&lst->key_idr_mutex); + _ncr_key_item_put(key); + return -ENOMEM; + } desc = key->desc; - up(&lst->sem); + mutex_unlock(&lst->key_idr_mutex); ret = copy_to_user(arg, &desc, sizeof(desc)); if (unlikely(ret)) { - down(&lst->sem); - _ncr_key_unlink_item(key); - up(&lst->sem); + _ncr_key_remove(lst, desc); return -EFAULT; } return ret; @@ -206,27 +201,16 @@ err_limits: return ret; } - -int ncr_key_deinit(struct list_sem_st* lst, void __user* arg) +int ncr_key_deinit(struct ncr_lists *lst, void __user* arg) { ncr_key_t desc; - struct key_item_st * item, *tmp; if (unlikely(copy_from_user(&desc, arg, sizeof(desc)))) { err(); return -EFAULT; } - down(&lst->sem); - - list_for_each_entry_safe(item, tmp, &lst->list, list) { - if(item->desc == desc) { - _ncr_key_unlink_item(item); - break; - } - } - - up(&lst->sem); + _ncr_key_remove(lst, desc); return 0; } @@ -234,7 +218,7 @@ int ncr_key_deinit(struct list_sem_st* lst, void __user* arg) /* "exports" a key to a data item. If the key is not exportable * to userspace then the data item will also not be. */ -int ncr_key_export(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_export(struct ncr_lists *lst, void __user* arg) { struct ncr_key_data_st data; struct key_item_st* item = NULL; @@ -247,7 +231,7 @@ int ret; return -EFAULT; } - ret = ncr_key_item_get_read( &item, key_lst, data.key); + ret = ncr_key_item_get_read( &item, lst, data.key); if (ret < 0) { err(); return ret; @@ -329,7 +313,7 @@ fail: /* "imports" a key from a data item. If the key is not exportable * to userspace then the key item will also not be. */ -int ncr_key_import(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_import(struct ncr_lists *lst, void __user* arg) { struct ncr_key_data_st data; struct key_item_st* item = NULL; @@ -342,7 +326,7 @@ size_t tmp_size; return -EFAULT; } - ret = ncr_key_item_get_write( &item, key_lst, data.key); + ret = ncr_key_item_get_write( &item, lst, data.key); if (ret < 0) { err(); return ret; @@ -438,7 +422,7 @@ static void ncr_key_clear(struct key_item_st* item) /* Generate a secret key */ -int ncr_key_generate(struct list_sem_st* lst, void __user* arg) +int ncr_key_generate(struct ncr_lists *lst, void __user* arg) { struct ncr_key_generate_st gen; struct key_item_st* item = NULL; @@ -500,7 +484,7 @@ fail: return ret; } -int ncr_key_info(struct list_sem_st* lst, void __user* arg) +int ncr_key_info(struct ncr_lists *lst, void __user* arg) { struct ncr_key_info_st info; struct key_item_st* item = NULL; @@ -535,7 +519,7 @@ fail: return ret; } -int ncr_key_generate_pair(struct list_sem_st* lst, void __user* arg) +int ncr_key_generate_pair(struct ncr_lists *lst, void __user* arg) { struct ncr_key_generate_st gen; struct key_item_st* private = NULL; @@ -602,7 +586,7 @@ fail: /* "exports" a key to a data item. If the key is not exportable * to userspace then the data item will also not be. */ -int ncr_key_derive(struct list_sem_st* key_lst, void __user* arg) +int ncr_key_derive(struct ncr_lists *lst, void __user* arg) { struct ncr_key_derivation_params_st data; int ret; @@ -614,13 +598,13 @@ struct key_item_st* newkey = NULL; return -EFAULT; } - ret = ncr_key_item_get_read( &key, key_lst, data.key); + ret = ncr_key_item_get_read( &key, lst, data.key); if (ret < 0) { err(); return ret; } - ret = ncr_key_item_get_write( &newkey, key_lst, data.newkey); + ret = ncr_key_item_get_write( &newkey, lst, data.newkey); if (ret < 0) { err(); goto fail; diff --git a/crypto/userspace/ncr-limits.c b/crypto/userspace/ncr-limits.c index a1ad7e36411..ff8ff59876f 100644 --- a/crypto/userspace/ncr-limits.c +++ b/crypto/userspace/ncr-limits.c @@ -22,6 +22,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <linux/hash.h> +#include <linux/mutex.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/highmem.h> @@ -44,53 +46,76 @@ static unsigned int max_per_process[] = { }; struct limit_user_item_st { - struct list_head list; + struct hlist_node hlist; uid_t uid; - limits_type_t type; - atomic_t cnt; + atomic_t cnt[NUM_LIMIT_TYPES]; }; struct limit_process_item_st { - struct list_head list; + struct hlist_node hlist; pid_t pid; - limits_type_t type; - atomic_t cnt; + atomic_t cnt[NUM_LIMIT_TYPES]; }; -struct limit_st { - struct list_sem_st users; - struct list_sem_st processes; -}; +static struct mutex user_limit_mutex; +#define USER_LIMIT_HASH_BITS 7 +#define USER_LIMIT_TABLE_SIZE (1 << USER_LIMIT_HASH_BITS) +static struct hlist_head user_limit_table[USER_LIMIT_TABLE_SIZE]; -static struct limit_st limits; +static struct hlist_head *user_limit_hash(uid_t uid) +{ + return &user_limit_table[hash_long(uid, USER_LIMIT_HASH_BITS)]; +} + +static struct mutex process_limit_mutex; +#define PROCESS_LIMIT_HASH_BITS 9 +#define PROCESS_LIMIT_TABLE_SIZE (1 << PROCESS_LIMIT_HASH_BITS) +static struct hlist_head process_limit_table[PROCESS_LIMIT_TABLE_SIZE]; + +static struct hlist_head *process_limit_hash(pid_t pid) +{ + return &process_limit_table[hash_long(pid, PROCESS_LIMIT_HASH_BITS)]; +} void ncr_limits_init(void) { - init_MUTEX(&limits.users.sem); - INIT_LIST_HEAD(&limits.users.list); + size_t i; - init_MUTEX(&limits.processes.sem); - INIT_LIST_HEAD(&limits.processes.list); + mutex_init(&user_limit_mutex); + for (i = 0; i < USER_LIMIT_TABLE_SIZE; i++) + INIT_HLIST_HEAD(&user_limit_table[i]); + + mutex_init(&process_limit_mutex); + for (i = 0; i < PROCESS_LIMIT_TABLE_SIZE; i++) + INIT_HLIST_HEAD(&process_limit_table[i]); } void ncr_limits_deinit(void) { -struct limit_process_item_st* pitem, *ptmp; -struct limit_user_item_st* uitem, *utmp; - - down(&limits.users.sem); - list_for_each_entry_safe(uitem, utmp, &limits.users.list, list) { - list_del(&uitem->list); - kfree(uitem); +struct limit_process_item_st* pitem; +struct limit_user_item_st* uitem; +struct hlist_node *pos, *tmp; +size_t i; + + mutex_lock(&user_limit_mutex); + for (i = 0; i < USER_LIMIT_TABLE_SIZE; i++) { + hlist_for_each_entry_safe(uitem, pos, tmp, &user_limit_table[i], + hlist) { + hlist_del(&uitem->hlist); + kfree(uitem); + } } - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); - down(&limits.processes.sem); - list_for_each_entry_safe(pitem, ptmp, &limits.processes.list, list) { - list_del(&pitem->list); - kfree(pitem); + mutex_lock(&process_limit_mutex); + for (i = 0; i < PROCESS_LIMIT_TABLE_SIZE; i++) { + hlist_for_each_entry_safe(pitem, pos, tmp, + &process_limit_table[i], hlist) { + hlist_del(&pitem->hlist); + kfree(pitem); + } } - up(&limits.processes.sem); + mutex_unlock(&process_limit_mutex); } @@ -98,79 +123,94 @@ int ncr_limits_add_and_check(uid_t uid, pid_t pid, limits_type_t type) { struct limit_process_item_st* pitem; struct limit_user_item_st* uitem; +struct hlist_head *user_head, *process_head; +struct hlist_node *pos; int add = 1; int ret; + BUG_ON(type >= NUM_LIMIT_TYPES); - down(&limits.users.sem); - list_for_each_entry(uitem, &limits.users.list, list) { - if (uitem->uid == uid && uitem->type == type) { + user_head = user_limit_hash(uid); + mutex_lock(&user_limit_mutex); + hlist_for_each_entry(uitem, pos, user_head, hlist) { + if (uitem->uid == uid) { add = 0; - if (atomic_add_unless(&uitem->cnt, 1, max_per_user[type])==0) { + if (atomic_add_unless(&uitem->cnt[type], 1, max_per_user[type])==0) { err(); - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); return -EPERM; } + break; } } if (add) { + size_t i; + uitem = kmalloc( sizeof(*uitem), GFP_KERNEL); if (uitem == NULL) { err(); - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); return -ENOMEM; } uitem->uid = uid; - uitem->type = type; - atomic_set(&uitem->cnt, 1); + for (i = 0; i < NUM_LIMIT_TYPES; i++) + atomic_set(&uitem->cnt[i], 0); + atomic_set(&uitem->cnt[type], 1); - list_add(&uitem->list, &limits.users.list); + hlist_add_head(&uitem->hlist, user_head); } - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); add = 1; /* check process limits */ - down(&limits.processes.sem); - list_for_each_entry(pitem, &limits.processes.list, list) { - if (pitem->pid == pid && pitem->type == type) { + process_head = process_limit_hash(uid); + mutex_lock(&process_limit_mutex); + hlist_for_each_entry(pitem, pos, process_head, hlist) { + if (pitem->pid == pid) { add = 0; - if (atomic_add_unless(&pitem->cnt, 1, max_per_process[type])==0) { + if (atomic_add_unless(&pitem->cnt[type], 1, max_per_process[type])==0) { err(); - up(&limits.processes.sem); + mutex_unlock(&process_limit_mutex); ret = -EPERM; goto restore_user; } + break; } } if (add) { + size_t i; + pitem = kmalloc(sizeof(*pitem), GFP_KERNEL); if (pitem == NULL) { err(); - up(&limits.processes.sem); + mutex_unlock(&process_limit_mutex); ret = -ENOMEM; goto restore_user; } pitem->pid = pid; - pitem->type = type; - atomic_set(&pitem->cnt, 1); + for (i = 0; i < NUM_LIMIT_TYPES; i++) + atomic_set(&pitem->cnt[i], 0); + atomic_set(&pitem->cnt[type], 1); - list_add(&pitem->list, &limits.processes.list); + hlist_add_head(&pitem->hlist, process_head); } - up(&limits.processes.sem); + mutex_unlock(&process_limit_mutex); return 0; restore_user: - down(&limits.users.sem); - list_for_each_entry(uitem, &limits.users.list, list) { - if (uitem->uid == uid && uitem->type == type) - atomic_dec(&uitem->cnt); + mutex_lock(&user_limit_mutex); + hlist_for_each_entry(uitem, pos, user_head, hlist) { + if (uitem->uid == uid) { + atomic_dec(&uitem->cnt[type]); + break; + } } - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); return ret; } @@ -178,23 +218,30 @@ void ncr_limits_remove(uid_t uid, pid_t pid, limits_type_t type) { struct limit_process_item_st* pitem; struct limit_user_item_st* uitem; - - down(&limits.users.sem); - list_for_each_entry(uitem, &limits.users.list, list) { - if (uitem->uid == uid && uitem->type == type) { - atomic_dec(&uitem->cnt); +struct hlist_head *hhead; +struct hlist_node *pos; + + BUG_ON(type >= NUM_LIMIT_TYPES); + hhead = user_limit_hash(uid); + mutex_lock(&user_limit_mutex); + hlist_for_each_entry(uitem, pos, hhead, hlist) { + if (uitem->uid == uid) { + atomic_dec(&uitem->cnt[type]); + break; } } - up(&limits.users.sem); + mutex_unlock(&user_limit_mutex); /* check process limits */ - down(&limits.processes.sem); - list_for_each_entry(pitem, &limits.processes.list, list) { - if (pitem->pid == pid && pitem->type == type) { - atomic_dec(&pitem->cnt); + hhead = process_limit_hash(uid); + mutex_lock(&process_limit_mutex); + hlist_for_each_entry(pitem, pos, hhead, hlist) { + if (pitem->pid == pid) { + atomic_dec(&pitem->cnt[type]); + break; } } - up(&limits.processes.sem); + mutex_unlock(&process_limit_mutex); return; } diff --git a/crypto/userspace/ncr-pk.c b/crypto/userspace/ncr-pk.c index e4529b30d7f..0a394a2b7a6 100644 --- a/crypto/userspace/ncr-pk.c +++ b/crypto/userspace/ncr-pk.c @@ -614,6 +614,10 @@ void * input, *output; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: + if (ctx->sign_hash == NULL) { + err(); + return -EINVAL; + } cret = rsa_sign_hash_ex( input, isg_size, output, &osize, ctx->type, ctx->sign_hash, ctx->salt_len, &ctx->key->key.pk.rsa); if (cret != CRYPT_OK) { @@ -674,6 +678,10 @@ uint8_t* sig; switch(ctx->algorithm->algo) { case NCR_ALG_RSA: + if (ctx->sign_hash == NULL) { + err(); + return -EINVAL; + } cret = rsa_verify_hash_ex( sig, sign_sg_size, hash, hash_size, ctx->type, ctx->sign_hash, ctx->salt_len, &stat, &ctx->key->key.pk.rsa); diff --git a/crypto/userspace/ncr-sessions.c b/crypto/userspace/ncr-sessions.c index 365935fe2bf..3109292cfc2 100644 --- a/crypto/userspace/ncr-sessions.c +++ b/crypto/userspace/ncr-sessions.c @@ -31,53 +31,39 @@ #include <linux/scatterlist.h> static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_op_st* op); -static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc); +static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc); -void ncr_sessions_list_deinit(struct list_sem_st* lst) +static int session_list_deinit_fn(int id, void *item, void *unused) { - if(lst) { - struct session_item_st * item, *tmp; - - down(&lst->sem); - - list_for_each_entry_safe(item, tmp, &lst->list, list) { - list_del(&item->list); - _ncr_sessions_item_put( item); /* decrement ref count */ - } - up(&lst->sem); - - } + (void)unused; + _ncr_sessions_item_put(item); + return 0; } -/* must be called with data semaphore down - */ -static ncr_session_t _ncr_sessions_get_new_desc( struct list_sem_st* lst) +void ncr_sessions_list_deinit(struct ncr_lists *lst) { -struct session_item_st* item; -int mx = 1; - - list_for_each_entry(item, &lst->list, list) { - mx = max(mx, item->desc); - } - mx++; - - return mx; + /* The mutex is not necessary, but doesn't hurt and makes it easier to + verify locking correctness. */ + mutex_lock(&lst->session_idr_mutex); + idr_for_each(&lst->session_idr, session_list_deinit_fn, NULL); + idr_remove_all(&lst->session_idr); + idr_destroy(&lst->session_idr); + mutex_unlock(&lst->session_idr_mutex); } /* returns the data item corresponding to desc */ -struct session_item_st* ncr_sessions_item_get( struct list_sem_st* lst, ncr_session_t desc) +struct session_item_st* ncr_sessions_item_get(struct ncr_lists *lst, ncr_session_t desc) { struct session_item_st* item; - down(&lst->sem); - list_for_each_entry(item, &lst->list, list) { - if (item->desc == desc) { - atomic_inc(&item->refcnt); - up(&lst->sem); - return item; - } + mutex_lock(&lst->session_idr_mutex); + item = idr_find(&lst->session_idr, desc); + if (item != NULL) { + atomic_inc(&item->refcnt); + mutex_unlock(&lst->session_idr_mutex); + return item; } - up(&lst->sem); + mutex_unlock(&lst->session_idr_mutex); err(); return NULL; @@ -97,7 +83,7 @@ void _ncr_sessions_item_put( struct session_item_st* item) } } -struct session_item_st* ncr_session_new(struct list_sem_st* lst) +struct session_item_st* ncr_session_new(struct ncr_lists *lst) { struct session_item_st* sess; @@ -114,23 +100,31 @@ struct session_item_st* ncr_session_new(struct list_sem_st* lst) sizeof(struct scatterlist), GFP_KERNEL); if (sess->sg == NULL || sess->pages == NULL) { err(); - kfree(sess->sg); - kfree(sess->pages); - kfree(sess); - return NULL; + goto err_sess; } init_MUTEX(&sess->mem_mutex); atomic_set(&sess->refcnt, 2); /* One for lst->list, one for "sess" */ - down(&lst->sem); - - sess->desc = _ncr_sessions_get_new_desc(lst); - list_add(&sess->list, &lst->list); - - up(&lst->sem); + mutex_lock(&lst->session_idr_mutex); + /* idr_pre_get() should preallocate enough, and, due to + session_idr_mutex, nobody else can use the preallocated data. + Therefore the loop recommended in idr_get_new() documentation is not + necessary. */ + if (idr_pre_get(&lst->session_idr, GFP_KERNEL) == 0 || + idr_get_new(&lst->session_idr, sess, &sess->desc) != 0) { + mutex_unlock(&lst->session_idr_mutex); + goto err_sess; + } + mutex_unlock(&lst->session_idr_mutex); return sess; + +err_sess: + kfree(sess->sg); + kfree(sess->pages); + kfree(sess); + return NULL; } static const struct algo_properties_st algo_properties[] = { @@ -226,7 +220,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses int ret; const struct algo_properties_st *sign_hash; - ns = ncr_session_new(&lists->sessions); + ns = ncr_session_new(lists); if (ns == NULL) { err(); return -ENOMEM; @@ -250,7 +244,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } /* read key */ - ret = ncr_key_item_get_read( &ns->key, &lists->key, session->key); + ret = ncr_key_item_get_read( &ns->key, lists, session->key); if (ret < 0) { err(); goto fail; @@ -319,7 +313,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses } else { /* read key */ - ret = ncr_key_item_get_read( &ns->key, &lists->key, session->key); + ret = ncr_key_item_get_read( &ns->key, lists, session->key); if (ret < 0) { err(); goto fail; @@ -390,7 +384,7 @@ static int _ncr_session_init(struct ncr_lists* lists, struct ncr_session_st* ses fail: if (ret < 0) { - _ncr_session_remove(&lists->sessions, ns->desc); + _ncr_session_remove(lists, ns->desc); } _ncr_sessions_item_put(ns); @@ -416,7 +410,7 @@ int ncr_session_init(struct ncr_lists* lists, void __user* arg) ret = copy_to_user( arg, &session, sizeof(session)); if (unlikely(ret)) { err(); - _ncr_session_remove(&lists->sessions, session.ses); + _ncr_session_remove(lists, session.ses); return -EFAULT; } return ret; @@ -479,23 +473,18 @@ int ret; return 0; } -static void _ncr_session_remove(struct list_sem_st* lst, ncr_session_t desc) +static void _ncr_session_remove(struct ncr_lists *lst, ncr_session_t desc) { - struct session_item_st * item, *tmp; + struct session_item_st * item; - down(&lst->sem); - - list_for_each_entry_safe(item, tmp, &lst->list, list) { - if(item->desc == desc) { - list_del(&item->list); - _ncr_sessions_item_put( item); /* decrement ref count */ - break; - } - } - - up(&lst->sem); + mutex_lock(&lst->session_idr_mutex); + item = idr_find(&lst->session_idr, desc); + if (item != NULL) + idr_remove(&lst->session_idr, desc); /* Steal the reference */ + mutex_unlock(&lst->session_idr_mutex); - return; + if (item != NULL) + _ncr_sessions_item_put(item); } static int _ncr_session_grow_pages(struct session_item_st *ses, int pagecount) @@ -630,7 +619,7 @@ static int _ncr_session_update(struct ncr_lists* lists, struct ncr_session_op_st unsigned osg_cnt=0, isg_cnt=0; size_t isg_size, osg_size; - sess = ncr_sessions_item_get( &lists->sessions, op->ses); + sess = ncr_sessions_item_get(lists, op->ses); if (sess == NULL) { err(); return -EINVAL; @@ -750,7 +739,7 @@ static int _ncr_session_final(struct ncr_lists* lists, struct ncr_session_op_st* void __user * udata = NULL; size_t *udata_size; - sess = ncr_sessions_item_get( &lists->sessions, op->ses); + sess = ncr_sessions_item_get(lists, op->ses); if (sess == NULL) { err(); return -EINVAL; @@ -900,7 +889,7 @@ fail: } _ncr_sessions_item_put(sess); - _ncr_session_remove(&lists->sessions, op->ses); + _ncr_session_remove(lists, op->ses); return ret; } @@ -913,14 +902,14 @@ static int _ncr_session_update_key(struct ncr_lists* lists, struct ncr_session_o struct session_item_st* sess; struct key_item_st* key = NULL; - sess = ncr_sessions_item_get( &lists->sessions, op->ses); + sess = ncr_sessions_item_get(lists, op->ses); if (sess == NULL) { err(); return -EINVAL; } /* read key */ - ret = ncr_key_item_get_read( &key, &lists->key, op->data.kdata.input); + ret = ncr_key_item_get_read( &key, lists, op->data.kdata.input); if (ret < 0) { err(); goto fail; diff --git a/crypto/userspace/ncr.c b/crypto/userspace/ncr.c index 7608312ed7b..d4eb83fb2b0 100644 --- a/crypto/userspace/ncr.c +++ b/crypto/userspace/ncr.c @@ -52,11 +52,11 @@ void* ncr_init_lists(void) memset(lst, 0, sizeof(*lst)); - init_MUTEX(&lst->key.sem); - INIT_LIST_HEAD(&lst->key.list); + mutex_init(&lst->key_idr_mutex); + idr_init(&lst->key_idr); - init_MUTEX(&lst->sessions.sem); - INIT_LIST_HEAD(&lst->sessions.list); + mutex_init(&lst->session_idr_mutex); + idr_init(&lst->session_idr); return lst; } @@ -64,8 +64,8 @@ void* ncr_init_lists(void) void ncr_deinit_lists(struct ncr_lists *lst) { if(lst) { - ncr_key_list_deinit(&lst->key); - ncr_sessions_list_deinit(&lst->sessions); + ncr_key_list_deinit(lst); + ncr_sessions_list_deinit(lst); kfree(lst); } } @@ -127,25 +127,25 @@ ncr_ioctl(struct ncr_lists* lst, struct file *filp, switch (cmd) { case NCRIO_KEY_INIT: - return ncr_key_init(&lst->key, arg); + return ncr_key_init(lst, arg); case NCRIO_KEY_DEINIT: - return ncr_key_deinit(&lst->key, arg); + return ncr_key_deinit(lst, arg); case NCRIO_KEY_GENERATE: - return ncr_key_generate(&lst->key, arg); + return ncr_key_generate(lst, arg); case NCRIO_KEY_EXPORT: - return ncr_key_export(&lst->key, arg); + return ncr_key_export(lst, arg); case NCRIO_KEY_IMPORT: - return ncr_key_import(&lst->key, arg); + return ncr_key_import(lst, arg); case NCRIO_KEY_GET_INFO: - return ncr_key_info(&lst->key, arg); + return ncr_key_info(lst, arg); case NCRIO_KEY_WRAP: - return ncr_key_wrap(&lst->key, arg); + return ncr_key_wrap(lst, arg); case NCRIO_KEY_UNWRAP: - return ncr_key_unwrap(&lst->key, arg); + return ncr_key_unwrap(lst, arg); case NCRIO_KEY_STORAGE_WRAP: - return ncr_key_storage_wrap(&lst->key, arg); + return ncr_key_storage_wrap(lst, arg); case NCRIO_KEY_STORAGE_UNWRAP: - return ncr_key_storage_unwrap(&lst->key, arg); + return ncr_key_storage_unwrap(lst, arg); case NCRIO_SESSION_INIT: return ncr_session_init(lst, arg); case NCRIO_SESSION_UPDATE: @@ -158,9 +158,9 @@ ncr_ioctl(struct ncr_lists* lst, struct file *filp, case NCRIO_MASTER_KEY_SET: return ncr_master_key_set(arg); case NCRIO_KEY_GENERATE_PAIR: - return ncr_key_generate_pair(&lst->key, arg); + return ncr_key_generate_pair(lst, arg); case NCRIO_KEY_DERIVE: - return ncr_key_derive(&lst->key, arg); + return ncr_key_derive(lst, arg); default: return -EINVAL; } |