aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt13
-rw-r--r--Makefile.am14
-rw-r--r--docs/src/misc.rst45
-rw-r--r--include/uv.h26
-rw-r--r--src/random.c120
-rw-r--r--src/threadpool.c4
-rw-r--r--src/unix/internal.h6
-rw-r--r--src/unix/linux-syscalls.c25
-rw-r--r--src/unix/linux-syscalls.h1
-rw-r--r--src/unix/random-devurandom.c93
-rw-r--r--src/unix/random-getentropy.c57
-rw-r--r--src/unix/random-getrandom.c88
-rw-r--r--src/unix/random-sysctl.c91
-rw-r--r--src/win/internal.h2
-rw-r--r--src/win/util.c13
-rw-r--r--src/win/winapi.c11
-rw-r--r--src/win/winapi.h8
-rw-r--r--test/test-list.h8
-rw-r--r--test/test-random.c94
-rw-r--r--test/test-threadpool-cancel.c41
-rw-r--r--test/test.gyp1
-rw-r--r--uv.gyp15
22 files changed, 771 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6f18f339..0b62c57e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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()
diff --git a/Makefile.am b/Makefile.am
index 099b0efb..ab7b97b5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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
diff --git a/docs/src/misc.rst b/docs/src/misc.rst
index 3ecfce48..8e167e3e 100644
--- a/docs/src/misc.rst
+++ b/docs/src/misc.rst
@@ -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
diff --git a/include/uv.h b/include/uv.h
index ee45bcae..e9cf5d51 100644
--- a/include/uv.h
+++ b/include/uv.h
@@ -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)
diff --git a/src/random.c b/src/random.c
new file mode 100644
index 00000000..233b486d
--- /dev/null
+++ b/src/random.c
@@ -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;
+}
diff --git a/src/threadpool.c b/src/threadpool.c
index 7aa57550..a8f433f0 100644
--- a/src/threadpool.c
+++ b/src/threadpool.c
@@ -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;
diff --git a/src/unix/internal.h b/src/unix/internal.h
index 26061647..47f22000 100644
--- a/src/unix/internal.h
+++ b/src/unix/internal.h
@@ -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);
diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c
index 5637cf98..95038786 100644
--- a/src/unix/linux-syscalls.c
+++ b/src/unix/linux-syscalls.c
@@ -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
+}
diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h
index 7e58bfa2..b7729b82 100644
--- a/src/unix/linux-syscalls.h
+++ b/src/unix/linux-syscalls.h
@@ -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_ */
diff --git a/src/unix/random-devurandom.c b/src/unix/random-devurandom.c
new file mode 100644
index 00000000..bfc40d20
--- /dev/null
+++ b/src/unix/random-devurandom.c
@@ -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);
+}
diff --git a/src/unix/random-getentropy.c b/src/unix/random-getentropy.c
new file mode 100644
index 00000000..c45d9fd4
--- /dev/null
+++ b/src/unix/random-getentropy.c
@@ -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;
+}
diff --git a/src/unix/random-getrandom.c b/src/unix/random-getrandom.c
new file mode 100644
index 00000000..bcc94089
--- /dev/null
+++ b/src/unix/random-getrandom.c
@@ -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;
+}
diff --git a/src/unix/random-sysctl.c b/src/unix/random-sysctl.c
new file mode 100644
index 00000000..7af2e320
--- /dev/null
+++ b/src/unix/random-sysctl.c
@@ -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;
+}
diff --git a/src/win/internal.h b/src/win/internal.h
index 70ddaa53..058ddb8e 100644
--- a/src/win/internal.h
+++ b/src/win/internal.h
@@ -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.
diff --git a/src/win/util.c b/src/win/util.c
index d52dd99c..4bbeb315 100644
--- a/src/win/util.c
+++ b/src/win/util.c
@@ -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;
+}
diff --git a/src/win/winapi.c b/src/win/winapi.c
index 19e4377f..85a9de8a 100644
--- a/src/win/winapi.c
+++ b/src/win/winapi.c
@@ -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");
}
diff --git a/src/win/winapi.h b/src/win/winapi.h
index 322a212d..fcc70652 100644
--- a/src/win/winapi.h
+++ b/src/win/winapi.h
@@ -4590,6 +4590,11 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
PULONG ReturnLength);
/*
+ * Advapi32 headers
+ */
+typedef BOOLEAN (WINAPI *sRtlGenRandom)(PVOID Buffer, ULONG BufferLength);
+
+/*
* Kernel32 headers
*/
#ifndef FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
@@ -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;
diff --git a/test/test-list.h b/test/test-list.h
index b6066f27..ad94c52d 100644
--- a/test/test-list.h
+++ b/test/test-list.h
@@ -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)
diff --git a/test/test-random.c b/test/test-random.c
new file mode 100644
index 00000000..2e3ce442
--- /dev/null
+++ b/test/test-random.c
@@ -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;
+}
diff --git a/test/test-threadpool-cancel.c b/test/test-threadpool-cancel.c
index dd13d8ae..be252c6f 100644
--- a/test/test-threadpool-cancel.c
+++ b/test/test-threadpool-cancel.c
@@ -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];
diff --git a/test/test.gyp b/test/test.gyp
index 60792ad6..19aa910e 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -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',
diff --git a/uv.gyp b/uv.gyp
index 75a6d978..051bdc93 100644
--- a/uv.gyp
+++ b/uv.gyp
@@ -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': {