unix,win: add uv_random()

Add an API for obtaining cryptographically strong random data from the
system PRNG.

Co-authored-by: Saúl Ibarra Corretgé <s@saghul.net>
Refs: https://github.com/libuv/libuv/pull/1055
PR-URL: https://github.com/libuv/libuv/pull/2347
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com>
This commit is contained in:
Ben Noordhuis 2019-06-22 15:18:17 +02:00 committed by Saúl Ibarra Corretgé
parent fd1502f563
commit 4ed2a78f0e
22 changed files with 771 additions and 5 deletions

View File

@ -14,6 +14,7 @@ set(uv_sources
src/fs-poll.c
src/idna.c
src/inet.c
src/random.c
src/strscpy.c
src/threadpool.c
src/timer.c
@ -108,6 +109,7 @@ set(uv_test_sources
test/test-process-title-threadsafe.c
test/test-process-title.c
test/test-queue-foreach-delete.c
test/test-random.c
test/test-ref.c
test/test-run-nowait.c
test/test-run-once.c
@ -236,6 +238,7 @@ else()
src/unix/pipe.c
src/unix/poll.c
src/unix/process.c
src/unix/random-devurandom.c
src/unix/signal.c
src/unix/stream.c
src/unix/tcp.c
@ -284,6 +287,14 @@ if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD")
list(APPEND uv_sources src/unix/bsd-ifaddrs.c src/unix/kqueue.c)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
list(APPEND uv_sources src/unix/random-getrandom.c)
endif()
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
list(APPEND uv_sources src/unix/random-getentropy.c)
endif()
if(APPLE)
list(APPEND uv_defines _DARWIN_UNLIMITED_SELECT=1 _DARWIN_USE_64_BIT_INODE=1)
list(APPEND uv_sources
@ -300,6 +311,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
src/unix/linux-inotify.c
src/unix/linux-syscalls.c
src/unix/procfs-exepath.c
src/unix/random-getrandom.c
src/unix/random-sysctl.c
src/unix/sysinfo-loadavg.c)
endif()

View File

@ -35,6 +35,7 @@ libuv_la_SOURCES = src/fs-poll.c \
src/idna.h \
src/inet.c \
src/queue.h \
src/random.c \
src/strscpy.c \
src/strscpy.h \
src/threadpool.c \
@ -105,6 +106,7 @@ libuv_la_SOURCES += src/unix/async.c \
src/unix/pipe.c \
src/unix/poll.c \
src/unix/process.c \
src/unix/random-devurandom.c \
src/unix/signal.c \
src/unix/spinlock.h \
src/unix/stream.c \
@ -240,6 +242,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-process-title.c \
test/test-process-title-threadsafe.c \
test/test-queue-foreach-delete.c \
test/test-random.c \
test/test-ref.c \
test/test-run-nowait.c \
test/test-run-once.c \
@ -414,7 +417,8 @@ libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
src/unix/darwin-proctitle.c \
src/unix/fsevents.c \
src/unix/kqueue.c \
src/unix/proctitle.c
src/unix/proctitle.c \
src/unix/random-getentropy.c
test_run_tests_LDFLAGS += -lutil
endif
@ -434,7 +438,8 @@ libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
src/unix/bsd-proctitle.c \
src/unix/freebsd.c \
src/unix/kqueue.c \
src/unix/posix-hrtime.c
src/unix/posix-hrtime.c \
src/unix/random-getrandom.c
test_run_tests_LDFLAGS += -lutil
endif
@ -465,6 +470,8 @@ libuv_la_SOURCES += src/unix/linux-core.c \
src/unix/linux-syscalls.h \
src/unix/procfs-exepath.c \
src/unix/proctitle.c \
src/unix/random-getrandom.c \
src/unix/random-sysctl.c \
src/unix/sysinfo-loadavg.c
test_run_tests_LDFLAGS += -lutil
endif
@ -498,7 +505,8 @@ libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
src/unix/bsd-proctitle.c \
src/unix/kqueue.c \
src/unix/openbsd.c \
src/unix/posix-hrtime.c
src/unix/posix-hrtime.c \
src/unix/random-getentropy.c
test_run_tests_LDFLAGS += -lutil
endif

View File

@ -46,6 +46,12 @@ Data types
Replacement function for :man:`free(3)`.
See :c:func:`uv_replace_allocator`.
.. c:type:: void (*uv_random_cb)(uv_random_t* req, int status, void* buf, size_t buflen)
Callback passed to :c:func:`uv_random`. `status` is non-zero in case of
error. The `buf` pointer is the same pointer that was passed to
:c:func:`uv_random`.
.. c:type:: uv_file
Cross platform representation of a file handle.
@ -191,6 +197,9 @@ Data types
char* value;
} uv_env_item_t;
.. c:type:: uv_random_t
Random data request type.
API
---
@ -648,3 +657,39 @@ API
argument to `gettimeofday()` is not supported, as it is considered obsolete.
.. versionadded:: 1.28.0
.. c:function:: int uv_random(uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags, uv_random_cb cb)
Fill `buf` with exactly `buflen` cryptographically strong random bytes
acquired from the system CSPRNG. `flags` is reserved for future extension
and must currently be 0.
Short reads are not possible. When less than `buflen` random bytes are
available, a non-zero error value is returned or passed to the callback.
The synchronous version may block indefinitely when not enough entropy
is available. The asynchronous version may not ever finish when the system
is low on entropy.
Sources of entropy:
- Windows: `RtlGenRandom <https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom>_`.
- Linux, Android: :man:`getrandom(2)` if available, or :man:`urandom(4)`
after reading from `/dev/random` once, or the `KERN_RANDOM`
:man:`sysctl(2)`.
- FreeBSD: `getrandom(2) <https://www.freebsd.org/cgi/man.cgi?query=getrandom&sektion=2>_`,
or `/dev/urandom` after reading from `/dev/random` once.
- macOS, OpenBSD: `getentropy(2) <https://man.openbsd.org/getentropy.2>_`
if available, or `/dev/urandom` after reading from `/dev/random` once.
- AIX: `/dev/random`.
- IBM i: `/dev/urandom`.
- Other UNIX: `/dev/urandom` after reading from `/dev/random` once.
:returns: 0 on success, or an error code < 0 on failure. The contents of
`buf` is undefined after an error.
.. note::
When using the synchronous version, both `loop` and `req` parameters
are not used and can be set to `NULL`.
.. versionadded:: 1.33.0

View File

@ -177,6 +177,7 @@ extern "C" {
XX(WORK, work) \
XX(GETADDRINFO, getaddrinfo) \
XX(GETNAMEINFO, getnameinfo) \
XX(RANDOM, random) \
typedef enum {
#define XX(code, _) UV_ ## code = UV__ ## code,
@ -234,6 +235,7 @@ typedef struct uv_connect_s uv_connect_t;
typedef struct uv_udp_send_s uv_udp_send_t;
typedef struct uv_fs_s uv_fs_t;
typedef struct uv_work_s uv_work_t;
typedef struct uv_random_s uv_random_t;
/* None of the above. */
typedef struct uv_env_item_s uv_env_item_t;
@ -330,6 +332,10 @@ typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req,
int status,
const char* hostname,
const char* service);
typedef void (*uv_random_cb)(uv_random_t* req,
int status,
void* buf,
size_t buflen);
typedef struct {
long tv_sec;
@ -1574,6 +1580,26 @@ UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size
UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size);
UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst);
struct uv_random_s {
UV_REQ_FIELDS
/* read-only */
uv_loop_t* loop;
/* private */
int status;
void* buf;
size_t buflen;
uv_random_cb cb;
struct uv__work work_req;
};
UV_EXTERN int uv_random(uv_loop_t* loop,
uv_random_t* req,
void *buf,
size_t buflen,
unsigned flags, /* For future extension; must be 0. */
uv_random_cb cb);
#if defined(IF_NAMESIZE)
# define UV_IF_NAMESIZE (IF_NAMESIZE + 1)
#elif defined(IFNAMSIZ)

120
src/random.c Normal file
View File

@ -0,0 +1,120 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "uv-common.h"
#ifdef _WIN32
# include "win/internal.h"
#else
# include "unix/internal.h"
#endif
static int uv__random(void* buf, size_t buflen) {
int rc;
#if defined(__PASE__)
rc = uv__random_readpath("/dev/urandom", buf, buflen);
#elif defined(_AIX)
rc = uv__random_readpath("/dev/random", buf, buflen);
#elif defined(__APPLE__) || defined(__OpenBSD__)
rc = uv__random_getentropy(buf, buflen);
if (rc == UV_ENOSYS)
rc = uv__random_devurandom(buf, buflen);
#elif defined(__FreeBSD__) || defined(__linux__)
rc = uv__random_getrandom(buf, buflen);
if (rc == UV_ENOSYS)
rc = uv__random_devurandom(buf, buflen);
# if defined(__linux__)
switch (rc) {
case UV_EACCES:
case UV_EIO:
case UV_ELOOP:
case UV_EMFILE:
case UV_ENFILE:
case UV_ENOENT:
case UV_EPERM:
rc = uv__random_sysctl(buf, buflen);
break;
}
# endif
#elif defined(_WIN32)
uv__once_init();
rc = uv__random_rtlgenrandom(buf, buflen);
#else
rc = uv__random_devurandom(buf, buflen);
#endif
return rc;
}
static void uv__random_work(struct uv__work* w) {
uv_random_t* req;
req = container_of(w, uv_random_t, work_req);
req->status = uv__random(req->buf, req->buflen);
}
static void uv__random_done(struct uv__work* w, int status) {
uv_random_t* req;
req = container_of(w, uv_random_t, work_req);
uv__req_unregister(req->loop, req);
if (status == 0)
status = req->status;
req->cb(req, status, req->buf, req->buflen);
}
int uv_random(uv_loop_t* loop,
uv_random_t* req,
void *buf,
size_t buflen,
unsigned flags,
uv_random_cb cb) {
if (buflen > 0x7FFFFFFFu)
return UV_E2BIG;
if (flags != 0)
return UV_EINVAL;
if (cb == NULL)
return uv__random(buf, buflen);
uv__req_init(loop, req, UV_RANDOM);
req->loop = loop;
req->status = 0;
req->cb = cb;
req->buf = buf;
req->buflen = buflen;
uv__work_submit(loop,
&req->work_req,
UV__WORK_CPU,
uv__random_work,
uv__random_done);
return 0;
}

View File

@ -372,6 +372,10 @@ int uv_cancel(uv_req_t* req) {
loop = ((uv_getnameinfo_t*) req)->loop;
wreq = &((uv_getnameinfo_t*) req)->work_req;
break;
case UV_RANDOM:
loop = ((uv_random_t*) req)->loop;
wreq = &((uv_random_t*) req)->work_req;
break;
case UV_WORK:
loop = ((uv_work_t*) req)->loop;
wreq = &((uv_work_t*) req)->work_req;

View File

@ -270,6 +270,12 @@ uv_handle_type uv__handle_type(int fd);
FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd);
/* random */
int uv__random_devurandom(void* buf, size_t buflen);
int uv__random_getrandom(void* buf, size_t buflen);
int uv__random_getentropy(void* buf, size_t buflen);
int uv__random_readpath(const char* path, void* buf, size_t buflen);
int uv__random_sysctl(void* buf, size_t buflen);
#if defined(__APPLE__)
int uv___stream_fd(const uv_stream_t* handle);

View File

@ -203,6 +203,22 @@
# endif
#endif /* __NR_statx */
#ifndef __NR_getrandom
# if defined(__x86_64__)
# define __NR_getrandom 318
# elif defined(__i386__)
# define __NR_getrandom 355
# elif defined(__aarch64__)
# define __NR_getrandom 384
# elif defined(__arm__)
# define __NR_getrandom (UV_SYSCALL_BASE + 384)
# elif defined(__ppc__)
# define __NR_getrandom 359
# elif defined(__s390__)
# define __NR_getrandom 349
# endif
#endif /* __NR_getrandom */
int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
#if defined(__i386__)
unsigned long args[4];
@ -367,3 +383,12 @@ int uv__statx(int dirfd,
return errno = ENOSYS, -1;
#endif
}
ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) {
#if defined(__NR_getrandom)
return syscall(__NR_getrandom, buf, buflen, flags);
#else
return errno = ENOSYS, -1;
#endif
}

View File

@ -148,5 +148,6 @@ int uv__statx(int dirfd,
int flags,
unsigned int mask,
struct uv__statx* statxbuf);
ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags);
#endif /* UV_LINUX_SYSCALL_H_ */

View File

@ -0,0 +1,93 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "internal.h"
#include <sys/stat.h>
#include <unistd.h>
static uv_once_t once = UV_ONCE_INIT;
static int status;
int uv__random_readpath(const char* path, void* buf, size_t buflen) {
struct stat s;
size_t pos;
ssize_t n;
int fd;
fd = uv__open_cloexec(path, O_RDONLY);
if (fd == -1)
return UV__ERR(errno);
if (fstat(fd, &s)) {
uv__close(fd);
return UV__ERR(errno);
}
if (!S_ISCHR(s.st_mode)) {
uv__close(fd);
return UV_EIO;
}
for (pos = 0; pos != buflen; pos += n) {
do
n = read(fd, (char*) buf + pos, buflen - pos);
while (n == -1 && errno == EINTR);
if (n == -1) {
uv__close(fd);
return UV__ERR(errno);
}
if (n == 0) {
uv__close(fd);
return UV_EIO;
}
}
uv__close(fd);
return 0;
}
static void uv__random_devurandom_init(void) {
char c;
/* Linux's and NetBSD's random(4) man page suggests applications should read
* at least once from /dev/random before switching to /dev/urandom in order
* to seed the system RNG. Reads from /dev/random can of course block
* indefinitely until entropy is available but that's the point.
*/
status = uv__random_readpath("/dev/random", &c, 1);
}
int uv__random_devurandom(void* buf, size_t buflen) {
uv_once(&once, uv__random_devurandom_init);
if (status != 0)
return status;
return uv__random_readpath("/dev/urandom", buf, buflen);
}

View File

@ -0,0 +1,57 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "internal.h"
#include <stddef.h>
#include <dlfcn.h>
typedef int (*uv__getentropy_cb)(void *, size_t);
static uv__getentropy_cb uv__getentropy;
static uv_once_t once = UV_ONCE_INIT;
static void uv__random_getentropy_init(void) {
uv__getentropy = (uv__getentropy_cb) dlsym(RTLD_DEFAULT, "getentropy");
}
int uv__random_getentropy(void* buf, size_t buflen) {
size_t pos;
size_t stride;
uv_once(&once, uv__random_getentropy_init);
if (uv__getentropy == NULL)
return UV_ENOSYS;
/* getentropy() returns an error for requests > 256 bytes. */
for (pos = 0, stride = 256; pos + stride < buflen; pos += stride)
if (uv__getentropy((char *) buf + pos, stride))
return UV__ERR(errno);
if (uv__getentropy((char *) buf + pos, buflen - pos))
return UV__ERR(errno);
return 0;
}

View File

@ -0,0 +1,88 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "internal.h"
#ifdef __linux__
#include "linux-syscalls.h"
#define uv__random_getrandom_init() 0
#else /* !__linux__ */
#include <stddef.h>
#include <dlfcn.h>
typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned);
static uv__getrandom_cb uv__getrandom;
static uv_once_t once = UV_ONCE_INIT;
static void uv__random_getrandom_init_once(void) {
uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom");
}
static int uv__random_getrandom_init(void) {
uv_once(&once, uv__random_getrandom_init_once);
if (uv__getrandom == NULL)
return UV_ENOSYS;
return 0;
}
#endif /* !__linux__ */
int uv__random_getrandom(void* buf, size_t buflen) {
ssize_t n;
size_t pos;
int rc;
rc = uv__random_getrandom_init();
if (rc != 0)
return rc;
for (pos = 0; pos != buflen; pos += n) {
do {
n = buflen - pos;
/* Most getrandom() implementations promise that reads <= 256 bytes
* will always succeed and won't be interrupted by signals.
* It's therefore useful to split it up in smaller reads because
* one big read may, in theory, continuously fail with EINTR.
*/
if (n > 256)
n = 256;
n = uv__getrandom((char *) buf + pos, n, 0);
} while (n == -1 && errno == EINTR);
if (n == -1)
return UV__ERR(errno);
if (n == 0)
return UV_EIO;
}
return 0;
}

91
src/unix/random-sysctl.c Normal file
View File

@ -0,0 +1,91 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "internal.h"
#include <errno.h>
#include <string.h>
#include <syscall.h>
#include <unistd.h>
struct uv__sysctl_args {
int* name;
int nlen;
void* oldval;
size_t* oldlenp;
void* newval;
size_t newlen;
unsigned long unused[4];
};
/* TODO(bnoordhuis) Use {CTL_KERN, KERN_ARND} on FreeBSD (and NetBSD?) */
int uv__random_sysctl(void* buf, size_t buflen) {
static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/};
struct uv__sysctl_args args;
char uuid[16];
char* p;
char* pe;
size_t n;
p = buf;
pe = p + buflen;
while (p < pe) {
memset(&args, 0, sizeof(args));
args.name = name;
args.nlen = ARRAY_SIZE(name);
args.oldval = uuid;
args.oldlenp = &n;
n = sizeof(uuid);
/* Emits a deprecation warning with some kernels but that seems like
* an okay trade-off for the fallback of the fallback: this function is
* only called when neither getrandom(2) nor /dev/urandom are available.
* Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL.
*/
if (syscall(SYS__sysctl, &args) == -1)
return UV__ERR(errno);
if (n != sizeof(uuid))
return UV_EIO; /* Can't happen. */
/* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain
* 4 and 5 bits of entropy, respectively. For ease of use, we skip those
* and only use 14 of the 16 bytes.
*/
uuid[6] = uuid[14];
uuid[8] = uuid[15];
n = pe - p;
if (n > 14)
n = 14;
memcpy(p, uuid, n);
p += n;
}
return 0;
}

View File

@ -280,6 +280,8 @@ int uv__getsockpeername(const uv_handle_t* handle,
int* namelen,
int delayed_error);
int uv__random_rtlgenrandom(void* buf, size_t buflen);
/*
* Process stdio handles.

View File

@ -1860,3 +1860,16 @@ int uv_gettimeofday(uv_timeval64_t* tv) {
tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
return 0;
}
int uv__random_rtlgenrandom(void* buf, size_t buflen) {
if (pRtlGenRandom == NULL)
return UV_ENOSYS;
if (buflen == 0)
return 0;
if (pRtlGenRandom(buf, buflen) == FALSE)
return UV_EIO;
return 0;
}

View File

@ -36,6 +36,9 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile;
sNtQuerySystemInformation pNtQuerySystemInformation;
sNtQueryInformationProcess pNtQueryInformationProcess;
/* Advapi32 function pointers */
sRtlGenRandom pRtlGenRandom;
/* Kernel32 function pointers */
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
@ -51,6 +54,7 @@ void uv_winapi_init(void) {
HMODULE powrprof_module;
HMODULE user32_module;
HMODULE kernel32_module;
HMODULE advapi32_module;
ntdll_module = GetModuleHandleA("ntdll.dll");
if (ntdll_module == NULL) {
@ -135,4 +139,11 @@ void uv_winapi_init(void) {
GetProcAddress(user32_module, "SetWinEventHook");
}
advapi32_module = GetModuleHandleA("advapi32.dll");
if (advapi32_module == NULL) {
uv_fatal_error(GetLastError(), "GetModuleHandleA");
}
pRtlGenRandom =
(sRtlGenRandom) GetProcAddress(advapi32_module, "SystemFunction036");
}

View File

@ -4589,6 +4589,11 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
ULONG Length,
PULONG ReturnLength);
/*
* Advapi32 headers
*/
typedef BOOLEAN (WINAPI *sRtlGenRandom)(PVOID Buffer, ULONG BufferLength);
/*
* Kernel32 headers
*/
@ -4731,6 +4736,9 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
extern sNtQuerySystemInformation pNtQuerySystemInformation;
extern sNtQueryInformationProcess pNtQueryInformationProcess;
/* Advapi32 function pointers */
extern sRtlGenRandom pRtlGenRandom;
/* Kernel32 function pointers */
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;

View File

@ -395,6 +395,7 @@ TEST_DECLARE (threadpool_queue_work_einval)
TEST_DECLARE (threadpool_multiple_event_loops)
TEST_DECLARE (threadpool_cancel_getaddrinfo)
TEST_DECLARE (threadpool_cancel_getnameinfo)
TEST_DECLARE (threadpool_cancel_random)
TEST_DECLARE (threadpool_cancel_work)
TEST_DECLARE (threadpool_cancel_fs)
TEST_DECLARE (threadpool_cancel_single)
@ -464,6 +465,9 @@ HELPER_DECLARE (pipe_echo_server)
TEST_DECLARE (queue_foreach_delete)
TEST_DECLARE (random_async)
TEST_DECLARE (random_sync)
TEST_DECLARE (handle_type_name)
TEST_DECLARE (req_type_name)
TEST_DECLARE (getters_setters)
@ -1000,6 +1004,7 @@ TASK_LIST_START
TEST_ENTRY_CUSTOM (threadpool_multiple_event_loops, 0, 0, 60000)
TEST_ENTRY (threadpool_cancel_getaddrinfo)
TEST_ENTRY (threadpool_cancel_getnameinfo)
TEST_ENTRY (threadpool_cancel_random)
TEST_ENTRY (threadpool_cancel_work)
TEST_ENTRY (threadpool_cancel_fs)
TEST_ENTRY (threadpool_cancel_single)
@ -1018,6 +1023,9 @@ TASK_LIST_START
TEST_ENTRY (queue_foreach_delete)
TEST_ENTRY (random_async)
TEST_ENTRY (random_sync)
TEST_ENTRY (handle_type_name)
TEST_ENTRY (req_type_name)
TEST_ENTRY (getters_setters)

94
test/test-random.c Normal file
View File

@ -0,0 +1,94 @@
/* Copyright libuv contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
#include <string.h>
static char scratch[256];
static int random_cb_called;
static void random_cb(uv_random_t* req, int status, void* buf, size_t buflen) {
char zero[sizeof(scratch)];
memset(zero, 0, sizeof(zero));
ASSERT(0 == status);
ASSERT(buf == (void*) scratch);
if (random_cb_called == 0) {
ASSERT(buflen == 0);
ASSERT(0 == memcmp(scratch, zero, sizeof(zero)));
} else {
ASSERT(buflen == sizeof(scratch));
/* Buy a lottery ticket if you manage to trip this assertion. */
ASSERT(0 != memcmp(scratch, zero, sizeof(zero)));
}
random_cb_called++;
}
TEST_IMPL(random_async) {
uv_random_t req;
uv_loop_t* loop;
loop = uv_default_loop();
ASSERT(UV_EINVAL == uv_random(loop, &req, scratch, sizeof(scratch), -1,
random_cb));
ASSERT(UV_E2BIG == uv_random(loop, &req, scratch, -1, -1, random_cb));
ASSERT(0 == uv_random(loop, &req, scratch, 0, 0, random_cb));
ASSERT(0 == random_cb_called);
ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
ASSERT(1 == random_cb_called);
ASSERT(0 == uv_random(loop, &req, scratch, sizeof(scratch), 0, random_cb));
ASSERT(1 == random_cb_called);
ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
ASSERT(2 == random_cb_called);
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(random_sync) {
char zero[256];
char buf[256];
ASSERT(UV_EINVAL == uv_random(NULL, NULL, buf, sizeof(buf), -1, NULL));
ASSERT(UV_E2BIG == uv_random(NULL, NULL, buf, -1, -1, NULL));
memset(buf, 0, sizeof(buf));
ASSERT(0 == uv_random(NULL, NULL, buf, sizeof(buf), 0, NULL));
/* Buy a lottery ticket if you manage to trip this assertion. */
memset(zero, 0, sizeof(zero));
ASSERT(0 != memcmp(buf, zero, sizeof(zero)));
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -37,6 +37,11 @@ struct cancel_info {
uv_timer_t timer_handle;
};
struct random_info {
uv_random_t random_req;
char buf[1];
};
static unsigned fs_cb_called;
static unsigned done_cb_called;
static unsigned done2_cb_called;
@ -143,6 +148,19 @@ static void nop_done_cb(uv_work_t* req, int status) {
}
static void nop_random_cb(uv_random_t* req, int status, void* buf, size_t len) {
struct random_info* ri;
ri = container_of(req, struct random_info, random_req);
ASSERT(status == UV_ECANCELED);
ASSERT(buf == (void*) ri->buf);
ASSERT(len == sizeof(ri->buf));
done_cb_called++;
}
TEST_IMPL(threadpool_cancel_getaddrinfo) {
uv_getaddrinfo_t reqs[4];
struct cancel_info ci;
@ -212,6 +230,29 @@ TEST_IMPL(threadpool_cancel_getnameinfo) {
}
TEST_IMPL(threadpool_cancel_random) {
struct random_info req;
uv_loop_t* loop;
saturate_threadpool();
loop = uv_default_loop();
ASSERT(0 == uv_random(loop,
&req.random_req,
&req.buf,
sizeof(req.buf),
0,
nop_random_cb));
ASSERT(0 == uv_cancel((uv_req_t*) &req));
ASSERT(0 == done_cb_called);
unblock_threadpool();
ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
ASSERT(1 == done_cb_called);
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(threadpool_cancel_work) {
struct cancel_info ci;
uv_work_t reqs[16];

View File

@ -90,6 +90,7 @@
'test-process-title.c',
'test-process-title-threadsafe.c',
'test-queue-foreach-delete.c',
'test-random.c',
'test-ref.c',
'test-run-nowait.c',
'test-run-once.c',

15
uv.gyp
View File

@ -75,6 +75,7 @@
'src/idna.h',
'src/inet.c',
'src/queue.h',
'src/random.c',
'src/strscpy.c',
'src/strscpy.h',
'src/threadpool.c',
@ -167,6 +168,7 @@
'src/unix/pipe.c',
'src/unix/poll.c',
'src/unix/process.c',
'src/unix/random-devurandom.c',
'src/unix/signal.c',
'src/unix/spinlock.h',
'src/unix/stream.c',
@ -226,7 +228,8 @@
'sources': [
'src/unix/darwin.c',
'src/unix/fsevents.c',
'src/unix/darwin-proctitle.c'
'src/unix/darwin-proctitle.c',
'src/unix/random-getentropy.c',
],
'defines': [
'_DARWIN_USE_64_BIT_INODE=1',
@ -241,6 +244,8 @@
'src/unix/linux-syscalls.c',
'src/unix/linux-syscalls.h',
'src/unix/procfs-exepath.c',
'src/unix/random-getrandom.c',
'src/unix/random-sysctl.c',
'src/unix/sysinfo-loadavg.c',
],
'link_settings': {
@ -320,8 +325,14 @@
[ 'OS=="freebsd" or OS=="dragonflybsd"', {
'sources': [ 'src/unix/freebsd.c' ],
}],
[ 'OS=="freebsd"', {
'sources': [ 'src/unix/random-getrandom.c' ],
}],
[ 'OS=="openbsd"', {
'sources': [ 'src/unix/openbsd.c' ],
'sources': [
'src/unix/openbsd.c',
'src/unix/random-getentropy.c',
],
}],
[ 'OS=="netbsd"', {
'link_settings': {