mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
hwrng: core - Fix page fault dead lock on mmap-ed hwrng
commit78aafb3884
upstream. There is a dead-lock in the hwrng device read path. This triggers when the user reads from /dev/hwrng into memory also mmap-ed from /dev/hwrng. The resulting page fault triggers a recursive read which then dead-locks. Fix this by using a stack buffer when calling copy_to_user. Reported-by: Edward Adam Davis <eadavis@qq.com> Reported-by: syzbot+c52ab18308964d248092@syzkaller.appspotmail.com Fixes:9996508b33
("hwrng: core - Replace u32 in driver API with byte array") Cc: <stable@vger.kernel.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
981a31b754
commit
c6a8111aac
@ -24,10 +24,13 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define RNG_MODULE_NAME "hw_random"
|
||||
|
||||
#define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES)
|
||||
|
||||
static struct hwrng *current_rng;
|
||||
/* the current rng has been explicitly chosen by user via sysfs */
|
||||
static int cur_rng_set_by_user;
|
||||
@ -59,7 +62,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
|
||||
|
||||
static size_t rng_buffer_size(void)
|
||||
{
|
||||
return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
|
||||
return RNG_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
static void add_early_randomness(struct hwrng *rng)
|
||||
@ -206,6 +209,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
|
||||
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
size_t size, loff_t *offp)
|
||||
{
|
||||
u8 buffer[RNG_BUFFER_SIZE];
|
||||
ssize_t ret = 0;
|
||||
int err = 0;
|
||||
int bytes_read, len;
|
||||
@ -233,34 +237,37 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
if (bytes_read < 0) {
|
||||
err = bytes_read;
|
||||
goto out_unlock_reading;
|
||||
}
|
||||
data_avail = bytes_read;
|
||||
}
|
||||
|
||||
if (!data_avail) {
|
||||
if (filp->f_flags & O_NONBLOCK) {
|
||||
} else if (bytes_read == 0 &&
|
||||
(filp->f_flags & O_NONBLOCK)) {
|
||||
err = -EAGAIN;
|
||||
goto out_unlock_reading;
|
||||
}
|
||||
} else {
|
||||
len = data_avail;
|
||||
|
||||
data_avail = bytes_read;
|
||||
}
|
||||
|
||||
len = data_avail;
|
||||
if (len) {
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
data_avail -= len;
|
||||
|
||||
if (copy_to_user(buf + ret, rng_buffer + data_avail,
|
||||
len)) {
|
||||
memcpy(buffer, rng_buffer + data_avail, len);
|
||||
}
|
||||
mutex_unlock(&reading_mutex);
|
||||
put_rng(rng);
|
||||
|
||||
if (len) {
|
||||
if (copy_to_user(buf + ret, buffer, len)) {
|
||||
err = -EFAULT;
|
||||
goto out_unlock_reading;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size -= len;
|
||||
ret += len;
|
||||
}
|
||||
|
||||
mutex_unlock(&reading_mutex);
|
||||
put_rng(rng);
|
||||
|
||||
if (need_resched())
|
||||
schedule_timeout_interruptible(1);
|
||||
@ -271,6 +278,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
}
|
||||
}
|
||||
out:
|
||||
memzero_explicit(buffer, sizeof(buffer));
|
||||
return ret ? : err;
|
||||
|
||||
out_unlock_reading:
|
||||
|
Loading…
Reference in New Issue
Block a user