Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eafd83b9 authored by Herbert Xu's avatar Herbert Xu Committed by Greg Kroah-Hartman
Browse files

hwrng: core - Fix page fault dead lock on mmap-ed hwrng



commit 78aafb3884f6bc6636efcc1760c891c8500b9922 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: default avatarEdward Adam Davis <eadavis@qq.com>
Reported-by: default avatar <syzbot+c52ab18308964d248092@syzkaller.appspotmail.com>
Fixes: 9996508b ("hwrng: core - Replace u32 in driver API with byte array")
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 489506a2
Loading
Loading
Loading
Loading
+21 −13
Original line number Diff line number Diff line
@@ -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)
@@ -202,6 +205,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;
@@ -229,34 +233,37 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
			if (bytes_read < 0) {
				err = bytes_read;
				goto out_unlock_reading;
			} else if (bytes_read == 0 &&
				   (filp->f_flags & O_NONBLOCK)) {
				err = -EAGAIN;
				goto out_unlock_reading;
			}

			data_avail = bytes_read;
		}

		if (!data_avail) {
			if (filp->f_flags & O_NONBLOCK) {
				err = -EAGAIN;
				goto out_unlock_reading;
			}
		} else {
		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);
@@ -267,6 +274,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: