Skip to content
This repository was archived by the owner on Jul 3, 2018. It is now read-only.

Commit 81f6fc4

Browse files
tytsoTheStrix
authored andcommitted
BACKPORT: random: introduce getrandom(2) system call
Almost clean cherry pick of c6e9d6f, includes change made by merge 0891ad8. The getrandom(2) system call was requested by the LibreSSL Portable developers. It is analoguous to the getentropy(2) system call in OpenBSD. The rationale of this system call is to provide resiliance against file descriptor exhaustion attacks, where the attacker consumes all available file descriptors, forcing the use of the fallback code where /dev/[u]random is not available. Since the fallback code is often not well-tested, it is better to eliminate this potential failure mode entirely. The other feature provided by this new system call is the ability to request randomness from the /dev/urandom entropy pool, but to block until at least 128 bits of entropy has been accumulated in the /dev/urandom entropy pool. Historically, the emphasis in the /dev/urandom development has been to ensure that urandom pool is initialized as quickly as possible after system boot, and preferably before the init scripts start execution. This is because changing /dev/urandom reads to block represents an interface change that could potentially break userspace which is not acceptable. In practice, on most x86 desktop and server systems, in general the entropy pool can be initialized before it is needed (and in modern kernels, we will printk a warning message if not). However, on an embedded system, this may not be the case. And so with this new interface, we can provide the functionality of blocking until the urandom pool has been initialized. Any userspace program which uses this new functionality must take care to assure that if it is used during the boot process, that it will not cause the init scripts or other portions of the system startup to hang indefinitely. SYNOPSIS #include <linux/random.h> int getrandom(void *buf, size_t buflen, unsigned int flags); DESCRIPTION The system call getrandom() fills the buffer pointed to by buf with up to buflen random bytes which can be used to seed user space random number generators (i.e., DRBG's) or for other cryptographic uses. It should not be used for Monte Carlo simulations or other programs/algorithms which are doing probabilistic sampling. If the GRND_RANDOM flags bit is set, then draw from the /dev/random pool instead of the /dev/urandom pool. The /dev/random pool is limited based on the entropy that can be obtained from environmental noise, so if there is insufficient entropy, the requested number of bytes may not be returned. If there is no entropy available at all, getrandom(2) will either block, or return an error with errno set to EAGAIN if the GRND_NONBLOCK bit is set in flags. If the GRND_RANDOM bit is not set, then the /dev/urandom pool will be used. Unlike using read(2) to fetch data from /dev/urandom, if the urandom pool has not been sufficiently initialized, getrandom(2) will block (or return -1 with the errno set to EAGAIN if the GRND_NONBLOCK bit is set in flags). The getentropy(2) system call in OpenBSD can be emulated using the following function: int getentropy(void *buf, size_t buflen) { int ret; if (buflen > 256) goto failure; ret = getrandom(buf, buflen, 0); if (ret < 0) return ret; if (ret == buflen) return 0; failure: errno = EIO; return -1; } RETURN VALUE On success, the number of bytes that was filled in the buf is returned. This may not be all the bytes requested by the caller via buflen if insufficient entropy was present in the /dev/random pool, or if the system call was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. ERRORS EINVAL An invalid flag was passed to getrandom(2) EFAULT buf is outside the accessible address space. EAGAIN The requested entropy was not available, and getentropy(2) would have blocked if the GRND_NONBLOCK flag was not set. EINTR While blocked waiting for entropy, the call was interrupted by a signal handler; see the description of how interrupted read(2) calls on "slow" devices are handled with and without the SA_RESTART flag in the signal(7) man page. NOTES For small requests (buflen <= 256) getrandom(2) will not return EINTR when reading from the urandom pool once the entropy pool has been initialized, and it will return all of the bytes that have been requested. This is the recommended way to use getrandom(2), and is designed for compatibility with OpenBSD's getentropy() system call. However, if you are using GRND_RANDOM, then getrandom(2) may block until the entropy accounting determines that sufficient environmental noise has been gathered such that getrandom(2) will be operating as a NRBG instead of a DRBG for those people who are working in the NIST SP 800-90 regime. Since it may block for a long time, these guarantees do *not* apply. The user may want to interrupt a hanging process using a signal, so blocking until all of the requested bytes are returned would be unfriendly. For this reason, the user of getrandom(2) MUST always check the return value, in case it returns some error, or if fewer bytes than requested was returned. In the case of !GRND_RANDOM and small request, the latter should never happen, but the careful userspace code (and all crypto code should be careful) should check for this anyway! Finally, unless you are doing long-term key generation (and perhaps not even then), you probably shouldn't be using GRND_RANDOM. The cryptographic algorithms used for /dev/urandom are quite conservative, and so should be sufficient for all purposes. The disadvantage of GRND_RANDOM is that it can block, and the increased complexity required to deal with partially fulfilled getrandom(2) requests. Signed-off-by: Theodore Ts'o <[email protected]> Reviewed-by: Zach Brown <[email protected]> Bug: http://b/29621447 Change-Id: I189ba74070dd6d918b0fdf83ff30bb74ec0f7556 (cherry picked from commit 4af712e)
1 parent dd01f87 commit 81f6fc4

File tree

6 files changed

+61
-10
lines changed

6 files changed

+61
-10
lines changed

arch/x86/syscalls/syscall_32.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,4 @@
361361
352 i386 sched_getattr sys_sched_getattr
362362
# 353 i386 renameat2 sys_renameat2
363363
354 i386 seccomp sys_seccomp
364+
355 i386 getrandom sys_getrandom

arch/x86/syscalls/syscall_64.tbl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@
324324
315 common sched_getattr sys_sched_getattr
325325
# 316 common renameat2 sys_renameat2
326326
317 common seccomp sys_seccomp
327+
318 common getrandom sys_getrandom
327328

328329
#
329330
# x32-specific system call numbers start at 512 to avoid cache impact

drivers/char/random.c

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@
257257
#include <linux/ptrace.h>
258258
#include <linux/kmemcheck.h>
259259
#include <linux/irq.h>
260+
#include <linux/syscalls.h>
261+
#include <linux/completion.h>
260262

261263
#include <asm/processor.h>
262264
#include <asm/uaccess.h>
@@ -405,6 +407,7 @@ static struct poolinfo {
405407
*/
406408
static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
407409
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
410+
static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait);
408411
static struct fasync_struct *fasync;
409412

410413
static bool debug;
@@ -612,12 +615,14 @@ static void credit_entropy_bits(struct entropy_store *r, int nbits)
612615
if (cmpxchg(&r->entropy_count, orig, entropy_count) != orig)
613616
goto retry;
614617

615-
if (!r->initialized && nbits > 0) {
616-
r->entropy_total += nbits;
617-
if (r->entropy_total > 128) {
618-
r->initialized = 1;
619-
if (r == &nonblocking_pool)
620-
prandom_reseed_late();
618+
r->entropy_total += nbits;
619+
if (!r->initialized && r->entropy_total > 128) {
620+
r->initialized = 1;
621+
r->entropy_total = 0;
622+
if (r == &nonblocking_pool) {
623+
prandom_reseed_late();
624+
wake_up_interruptible(&urandom_init_wait);
625+
pr_notice("random: %s pool is initialized\n", r->name);
621626
}
622627
}
623628

@@ -1026,13 +1031,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
10261031
{
10271032
ssize_t ret = 0, i;
10281033
__u8 tmp[EXTRACT_SIZE];
1034+
int large_request = (nbytes > 256);
10291035

10301036
trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_);
10311037
xfer_secondary_pool(r, nbytes);
10321038
nbytes = account(r, nbytes, 0, 0);
10331039

10341040
while (nbytes) {
1035-
if (need_resched()) {
1041+
if (large_request && need_resched()) {
10361042
if (signal_pending(current)) {
10371043
if (ret == 0)
10381044
ret = -ERESTARTSYS;
@@ -1166,7 +1172,7 @@ void rand_initialize_disk(struct gendisk *disk)
11661172
#endif
11671173

11681174
static ssize_t
1169-
random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1175+
_random_read(int nonblock, char __user *buf, size_t nbytes)
11701176
{
11711177
ssize_t n, retval = 0, count = 0;
11721178

@@ -1191,7 +1197,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
11911197
n*8, (nbytes-n)*8);
11921198

11931199
if (n == 0) {
1194-
if (file->f_flags & O_NONBLOCK) {
1200+
if (nonblock) {
11951201
retval = -EAGAIN;
11961202
break;
11971203
}
@@ -1222,6 +1228,12 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
12221228
return (count ? count : retval);
12231229
}
12241230

1231+
static ssize_t
1232+
random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
1233+
{
1234+
return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes);
1235+
}
1236+
12251237
static ssize_t
12261238
urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
12271239
{
@@ -1348,6 +1360,29 @@ const struct file_operations urandom_fops = {
13481360
.llseek = noop_llseek,
13491361
};
13501362

1363+
SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count,
1364+
unsigned int, flags)
1365+
{
1366+
if (flags & ~(GRND_NONBLOCK|GRND_RANDOM))
1367+
return -EINVAL;
1368+
1369+
if (count > INT_MAX)
1370+
count = INT_MAX;
1371+
1372+
if (flags & GRND_RANDOM)
1373+
return _random_read(flags & GRND_NONBLOCK, buf, count);
1374+
1375+
if (unlikely(nonblocking_pool.initialized == 0)) {
1376+
if (flags & GRND_NONBLOCK)
1377+
return -EAGAIN;
1378+
wait_event_interruptible(urandom_init_wait,
1379+
nonblocking_pool.initialized);
1380+
if (signal_pending(current))
1381+
return -ERESTARTSYS;
1382+
}
1383+
return urandom_read(NULL, buf, count, NULL);
1384+
}
1385+
13511386
/***************************************************************
13521387
* Random UUID interface
13531388
*

include/linux/syscalls.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,4 +872,8 @@ asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
872872
asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
873873
asmlinkage long sys_seccomp(unsigned int op, unsigned int flags,
874874
const char __user *uargs);
875+
876+
asmlinkage long sys_getrandom(char __user *buf, size_t count,
877+
unsigned int flags);
878+
875879
#endif

include/uapi/asm-generic/unistd.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,9 +703,11 @@ __SYSCALL(__NR_renameat2, sys_renameat2)
703703
*/
704704
#define __NR_seccomp 277
705705
__SYSCALL(__NR_seccomp, sys_seccomp)
706+
#define __NR_getrandom 278
707+
__SYSCALL(__NR_getrandom, sys_getrandom)
706708

707709
#undef __NR_syscalls
708-
#define __NR_syscalls 278
710+
#define __NR_syscalls 279
709711

710712
/*
711713
* All syscalls below here should go away really,

include/uapi/linux/random.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,13 @@ struct rnd_state {
4646

4747
/* Exported functions */
4848

49+
/*
50+
* Flags for getrandom(2)
51+
*
52+
* GRND_NONBLOCK Don't block and return EAGAIN instead
53+
* GRND_RANDOM Use the /dev/random pool instead of /dev/urandom
54+
*/
55+
#define GRND_NONBLOCK 0x0001
56+
#define GRND_RANDOM 0x0002
4957

5058
#endif /* _UAPI_LINUX_RANDOM_H */

0 commit comments

Comments
 (0)