From 16b369a91d0dd80be214b7f7801fbc51875454cc Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Mon, 25 May 2015 15:08:47 +0200 Subject: random: Blocking API for accessing nonblocking_pool The added API calls provide a synchronous function call get_blocking_random_bytes where the caller is blocked until the nonblocking_pool is initialized. CC: Andreas Steffen CC: Theodore Ts'o CC: Sandy Harris Signed-off-by: Stephan Mueller Signed-off-by: Herbert Xu --- include/linux/random.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux/random.h') diff --git a/include/linux/random.h b/include/linux/random.h index b05856e16b75..796267d56901 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -14,6 +14,7 @@ extern void add_input_randomness(unsigned int type, unsigned int code, extern void add_interrupt_randomness(int irq, int irq_flags); extern void get_random_bytes(void *buf, int nbytes); +extern void get_blocking_random_bytes(void *buf, int nbytes); extern void get_random_bytes_arch(void *buf, int nbytes); void generate_random_uuid(unsigned char uuid_out[16]); extern int random_int_secret_init(void); -- cgit v1.2.3-71-gd317 From 205a525c334295e3cd4cc7755fd2c0398e3a787f Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 9 Jun 2015 18:19:39 +0800 Subject: random: Add callback API for random pool readiness The get_blocking_random_bytes API is broken because the wait can be arbitrarily long (potentially forever) so there is no safe way of calling it from within the kernel. This patch replaces it with a callback API instead. The callback is invoked potentially from interrupt context so the user needs to schedule their own work thread if necessary. In addition to adding callbacks, they can also be removed as otherwise this opens up a way for user-space to allocate kernel memory with no bound (by opening algif_rng descriptors and then closing them). Signed-off-by: Herbert Xu --- drivers/char/random.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/random.h | 9 ++++++ 2 files changed, 87 insertions(+) (limited to 'include/linux/random.h') diff --git a/drivers/char/random.c b/drivers/char/random.c index 159d0700f7d8..a1576ed1d88e 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -409,6 +409,9 @@ static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait); static struct fasync_struct *fasync; +static DEFINE_SPINLOCK(random_ready_list_lock); +static LIST_HEAD(random_ready_list); + /********************************************************************** * * OS independent entropy store. Here are the functions which handle @@ -589,6 +592,22 @@ static void fast_mix(struct fast_pool *f) f->count++; } +static void process_random_ready_list(void) +{ + unsigned long flags; + struct random_ready_callback *rdy, *tmp; + + spin_lock_irqsave(&random_ready_list_lock, flags); + list_for_each_entry_safe(rdy, tmp, &random_ready_list, list) { + struct module *owner = rdy->owner; + + list_del_init(&rdy->list); + rdy->func(rdy); + module_put(owner); + } + spin_unlock_irqrestore(&random_ready_list_lock, flags); +} + /* * Credit (or debit) the entropy store with n bits of entropy. * Use credit_entropy_bits_safe() if the value comes from userspace @@ -660,6 +679,7 @@ retry: r->entropy_total = 0; if (r == &nonblocking_pool) { prandom_reseed_late(); + process_random_ready_list(); wake_up_all(&urandom_init_wait); pr_notice("random: %s pool is initialized\n", r->name); } @@ -1256,6 +1276,64 @@ void get_blocking_random_bytes(void *buf, int nbytes) } EXPORT_SYMBOL(get_blocking_random_bytes); +/* + * Add a callback function that will be invoked when the nonblocking + * pool is initialised. + * + * returns: 0 if callback is successfully added + * -EALREADY if pool is already initialised (callback not called) + * -ENOENT if module for callback is not alive + */ +int add_random_ready_callback(struct random_ready_callback *rdy) +{ + struct module *owner; + unsigned long flags; + int err = -EALREADY; + + if (likely(nonblocking_pool.initialized)) + return err; + + owner = rdy->owner; + if (!try_module_get(owner)) + return -ENOENT; + + spin_lock_irqsave(&random_ready_list_lock, flags); + if (nonblocking_pool.initialized) + goto out; + + owner = NULL; + + list_add(&rdy->list, &random_ready_list); + err = 0; + +out: + spin_unlock_irqrestore(&random_ready_list_lock, flags); + + module_put(owner); + + return err; +} +EXPORT_SYMBOL(add_random_ready_callback); + +/* + * Delete a previously registered readiness callback function. + */ +void del_random_ready_callback(struct random_ready_callback *rdy) +{ + unsigned long flags; + struct module *owner = NULL; + + spin_lock_irqsave(&random_ready_list_lock, flags); + if (!list_empty(&rdy->list)) { + list_del_init(&rdy->list); + owner = rdy->owner; + } + spin_unlock_irqrestore(&random_ready_list_lock, flags); + + module_put(owner); +} +EXPORT_SYMBOL(del_random_ready_callback); + /* * This function will use the architecture-specific hardware random * number generator if it is available. The arch-specific hw RNG will diff --git a/include/linux/random.h b/include/linux/random.h index 796267d56901..30e2aca0b16a 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -6,8 +6,15 @@ #ifndef _LINUX_RANDOM_H #define _LINUX_RANDOM_H +#include #include +struct random_ready_callback { + struct list_head list; + void (*func)(struct random_ready_callback *rdy); + struct module *owner; +}; + extern void add_device_randomness(const void *, unsigned int); extern void add_input_randomness(unsigned int type, unsigned int code, unsigned int value); @@ -15,6 +22,8 @@ extern void add_interrupt_randomness(int irq, int irq_flags); extern void get_random_bytes(void *buf, int nbytes); extern void get_blocking_random_bytes(void *buf, int nbytes); +extern int add_random_ready_callback(struct random_ready_callback *rdy); +extern void del_random_ready_callback(struct random_ready_callback *rdy); extern void get_random_bytes_arch(void *buf, int nbytes); void generate_random_uuid(unsigned char uuid_out[16]); extern int random_int_secret_init(void); -- cgit v1.2.3-71-gd317 From c2719503f5e1e6213d716bb078bdad01e28ebcbf Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Tue, 9 Jun 2015 18:19:42 +0800 Subject: random: Remove kernel blocking API This patch removes the kernel blocking API as it has been completely replaced by the callback API. Signed-off-by: Herbert Xu --- drivers/char/random.c | 12 ------------ include/linux/random.h | 1 - 2 files changed, 13 deletions(-) (limited to 'include/linux/random.h') diff --git a/drivers/char/random.c b/drivers/char/random.c index a1576ed1d88e..d0da5d852d41 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1264,18 +1264,6 @@ void get_random_bytes(void *buf, int nbytes) } EXPORT_SYMBOL(get_random_bytes); -/* - * Equivalent function to get_random_bytes with the difference that this - * function blocks the request until the nonblocking_pool is initialized. - */ -void get_blocking_random_bytes(void *buf, int nbytes) -{ - if (unlikely(nonblocking_pool.initialized == 0)) - wait_event(urandom_init_wait, nonblocking_pool.initialized); - extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0); -} -EXPORT_SYMBOL(get_blocking_random_bytes); - /* * Add a callback function that will be invoked when the nonblocking * pool is initialised. diff --git a/include/linux/random.h b/include/linux/random.h index 30e2aca0b16a..e651874df2c9 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -21,7 +21,6 @@ extern void add_input_randomness(unsigned int type, unsigned int code, extern void add_interrupt_randomness(int irq, int irq_flags); extern void get_random_bytes(void *buf, int nbytes); -extern void get_blocking_random_bytes(void *buf, int nbytes); extern int add_random_ready_callback(struct random_ready_callback *rdy); extern void del_random_ready_callback(struct random_ready_callback *rdy); extern void get_random_bytes_arch(void *buf, int nbytes); -- cgit v1.2.3-71-gd317