Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

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: 9996508b3353 ("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>

+22 -14
+22 -14
drivers/char/hw_random/core.c
··· 23 23 #include <linux/sched.h> 24 24 #include <linux/sched/signal.h> 25 25 #include <linux/slab.h> 26 + #include <linux/string.h> 26 27 #include <linux/uaccess.h> 27 28 28 29 #define RNG_MODULE_NAME "hw_random" 30 + 31 + #define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES) 29 32 30 33 static struct hwrng *current_rng; 31 34 /* the current rng has been explicitly chosen by user via sysfs */ ··· 61 58 62 59 static size_t rng_buffer_size(void) 63 60 { 64 - return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; 61 + return RNG_BUFFER_SIZE; 65 62 } 66 63 67 64 static void add_early_randomness(struct hwrng *rng) ··· 212 209 static ssize_t rng_dev_read(struct file *filp, char __user *buf, 213 210 size_t size, loff_t *offp) 214 211 { 212 + u8 buffer[RNG_BUFFER_SIZE]; 215 213 ssize_t ret = 0; 216 214 int err = 0; 217 215 int bytes_read, len; ··· 240 236 if (bytes_read < 0) { 241 237 err = bytes_read; 242 238 goto out_unlock_reading; 243 - } 244 - data_avail = bytes_read; 245 - } 246 - 247 - if (!data_avail) { 248 - if (filp->f_flags & O_NONBLOCK) { 239 + } else if (bytes_read == 0 && 240 + (filp->f_flags & O_NONBLOCK)) { 249 241 err = -EAGAIN; 250 242 goto out_unlock_reading; 251 243 } 252 - } else { 253 - len = data_avail; 244 + 245 + data_avail = bytes_read; 246 + } 247 + 248 + len = data_avail; 249 + if (len) { 254 250 if (len > size) 255 251 len = size; 256 252 257 253 data_avail -= len; 258 254 259 - if (copy_to_user(buf + ret, rng_buffer + data_avail, 260 - len)) { 255 + memcpy(buffer, rng_buffer + data_avail, len); 256 + } 257 + mutex_unlock(&reading_mutex); 258 + put_rng(rng); 259 + 260 + if (len) { 261 + if (copy_to_user(buf + ret, buffer, len)) { 261 262 err = -EFAULT; 262 - goto out_unlock_reading; 263 + goto out; 263 264 } 264 265 265 266 size -= len; 266 267 ret += len; 267 268 } 268 269 269 - mutex_unlock(&reading_mutex); 270 - put_rng(rng); 271 270 272 271 if (need_resched()) 273 272 schedule_timeout_interruptible(1); ··· 281 274 } 282 275 } 283 276 out: 277 + memzero_explicit(buffer, sizeof(buffer)); 284 278 return ret ? : err; 285 279 286 280 out_unlock_reading: