diff --git a/lib/libc/gen/arc4random.c b/lib/libc/gen/arc4random.c index a48c6f81938..79b15e9bdb7 100644 --- a/lib/libc/gen/arc4random.c +++ b/lib/libc/gen/arc4random.c @@ -111,8 +111,8 @@ arc4_addrandom(u_char *dat, int datlen) rs.j = rs.i; } -static size_t -arc4_sysctl(u_char *buf, size_t size) +size_t +__arc4_sysctl(u_char *buf, size_t size) { int mib[2]; size_t len, done; @@ -143,7 +143,7 @@ arc4_stir(void) arc4_init(); rs_initialized = 1; } - if (arc4_sysctl(rdat, KEYSIZE) != KEYSIZE) { + if (__arc4_sysctl(rdat, KEYSIZE) != KEYSIZE) { /* * The sysctl cannot fail. If it does fail on some FreeBSD * derivative or after some future change, just abort so that diff --git a/lib/libc/gen/getentropy.c b/lib/libc/gen/getentropy.c index 8484570ba78..ec5ef12ec77 100644 --- a/lib/libc/gen/getentropy.c +++ b/lib/libc/gen/getentropy.c @@ -37,6 +37,39 @@ __FBSDID("$FreeBSD$"); #include "libc_private.h" +/* + * If a newer libc is accidentally installed on an older kernel, provide high + * quality random data anyway. The sysctl interface is not as fast and does + * not block by itself, but is provided by even very old kernels. + */ +static int +getentropy_fallback(void *buf, size_t buflen) +{ + /* + * oldp (buf) == NULL has a special meaning for sysctl that results in + * no EFAULT. For compatibility with the kernel getrandom(2), detect + * this case and return the appropriate error. + */ + if (buf == NULL && buflen > 0) { + errno = EFAULT; + return (-1); + } + if (__arc4_sysctl(buf, buflen) != buflen) { + if (errno == EFAULT) + return (-1); + /* + * This cannot happen. _arc4_sysctl() spins until the random + * device is seeded and then repeatedly reads until the full + * request is satisfied. The only way for this to return a zero + * byte or short read is if sysctl(2) on the kern.arandom MIB + * fails. In this case, exceping the user-provided-a-bogus- + * buffer EFAULT, give up (like for arc4random(3)'s arc4_stir). + */ + abort(); + } + return (0); +} + int getentropy(void *buf, size_t buflen) { @@ -53,7 +86,7 @@ getentropy(void *buf, size_t buflen) if (errno == EINTR) continue; else if (errno == ENOSYS) - abort(); + return (getentropy_fallback(buf, buflen)); else return (-1); } diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index aaa40deeb08..0985b5ed874 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -405,6 +405,8 @@ int __sys_futimens(int fd, const struct timespec *times) __hidden; int __sys_utimensat(int fd, const char *path, const struct timespec *times, int flag) __hidden; +__size_t __arc4_sysctl(unsigned char *, __size_t); + /* execve() with PATH processing to implement posix_spawnp() */ int _execvpe(const char *, char * const *, char * const *); diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 1a711c99ba8..bfc0b7e7eb2 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -628,8 +628,6 @@ FBSDprivate_1.0 { __sys_getppid; _getpriority; __sys_getpriority; - _getrandom; - __sys_getrandom; _getresgid; __sys_getresgid; _getresuid; diff --git a/lib/libc/tests/gen/getentropy_test.c b/lib/libc/tests/gen/getentropy_test.c index 731bed77526..1290e6e5052 100644 --- a/lib/libc/tests/gen/getentropy_test.c +++ b/lib/libc/tests/gen/getentropy_test.c @@ -31,6 +31,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include @@ -77,6 +78,8 @@ ATF_TC_BODY(getentropy_sizes, tc) ATF_TP_ADD_TCS(tp) { + signal(SIGSYS, SIG_IGN); + ATF_TP_ADD_TC(tp, getentropy_count); ATF_TP_ADD_TC(tp, getentropy_fault); ATF_TP_ADD_TC(tp, getentropy_sizes);