/home/travis/build/MoarVM/MoarVM/src/platform/random.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Get random numbers from OS. Returns 1 if it succeeded and otherwise 0 |
2 | | * Does not block. Designed for getting small amounts of random data at a time */ |
3 | | #include <stddef.h> |
4 | | /* Solaris has both getrandom and getentropy. We use getrandom since getentropy |
5 | | * can block. Solaris has had getrandom() and getentropy() since 11.3 */ |
6 | | #if defined(__sun) |
7 | | #include <sys/random.h> |
8 | | /* On solaris, _GRND_ENTROPY is defined if getentropy/getrandom are available */ |
9 | | #if defined(_GRND_ENTROPY) |
10 | | #define MVM_random_use_getrandom 1 |
11 | | #endif |
12 | | #endif |
13 | | /* Linux added getrandom to the kernel in 3.17 */ |
14 | | #if defined(__linux__) |
15 | | #include <sys/syscall.h> |
16 | | #if defined(SYS_getrandom) |
17 | | /* With glibc you are supposed to declare _GNU_SOURCE to use the |
18 | | * syscall function */ |
19 | | #define _GNU_SOURCE |
20 | | #define GRND_NONBLOCK 0x01 |
21 | | #include <unistd.h> |
22 | | #define MVM_random_use_getrandom_syscall 1 |
23 | | #else |
24 | | #define MVM_random_use_urandom 1 |
25 | | #endif |
26 | | #endif |
27 | | /* FreeBSD added it with SVN revision 331279 Wed Mar 21, 2018 |
28 | | * This corresponds to __FreeBSD_version version identifier: 1200061. |
29 | | * https://svnweb.freebsd.org/base?view=revision&revision=r331279 */ |
30 | | #if defined(__FreeBSD__) |
31 | | #include <osreldate.h> |
32 | | #if __FreeBSD_version >= 1200061 |
33 | | #include <sys/random.h> |
34 | | #define MVM_random_use_getrandom |
35 | | #endif |
36 | | #endif |
37 | | /* OpenBSD's getentropy never blocks and always succeeds. OpenBSD has had |
38 | | * getentropy() since 5.6 */ |
39 | | #if defined(__OpenBSD__) |
40 | | #include <sys/param.h> |
41 | | #if OpenBSD >= 201301 |
42 | | #define MVM_random_use_getentropy |
43 | | #endif |
44 | | #endif |
45 | | /* MacOS has had getentropy() since 10.12 */ |
46 | | #if defined(__APPLE__) |
47 | | #include <AvailabilityMacros.h> |
48 | | #include <Availability.h> |
49 | | #if !defined(MAC_OS_X_VERSION_10_12) |
50 | | #define MAC_OS_X_VERSION_10_12 101200 |
51 | | #endif |
52 | | #if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 |
53 | | #include <sys/random.h> |
54 | | #define MVM_random_use_getentropy 1 |
55 | | #endif |
56 | | #endif |
57 | | /* Other info: |
58 | | * - All BSD's should support arc4random |
59 | | * - AIX is a Unix but has no arc4random, does have /dev/urandom. |
60 | | * - NetBSD: I have not found evidence it has getentropy() or getrandom() |
61 | | * Note: Uses __NetBSD_Version__ included from file <sys/param.h>. */ |
62 | | #include "moar.h" |
63 | | /* On Unix like platforms that don't support getrandom() or getentropy() |
64 | | * we defualt to /dev/urandom. On platforms that do support these calls, we |
65 | | * only use /dev/urandom if those calls fail. This is also important on Linux, |
66 | | * since if MoarVM was compiled on a kernel >= 3.17 it will be set to use the |
67 | | * syscall. If the syscall doesn't exist, the syscall wrapper will gracefully |
68 | | * return a false return value and we will fallback to /dev/urandom */ |
69 | | #if !defined(_WIN32) |
70 | | #include <unistd.h> |
71 | 144 | MVMint32 MVM_getrandom_urandom (MVMThreadContext *tc, void *out, size_t size) { |
72 | 144 | int fd = open("/dev/urandom", O_RDONLY); |
73 | 144 | ssize_t num_read = 0; |
74 | 144 | if (fd < 0 || (num_read = read(fd, out, size) <= 0)) { |
75 | 0 | if (fd) close(fd); |
76 | 0 | /* If using /dev/urandom fails (maybe we're in a chroot), on BSD's |
77 | 0 | * use arc4random, which is likely seeded from the system's random |
78 | 0 | * number generator */ |
79 | 0 | #if defined(BSD) |
80 | | #include <stdlib.h> |
81 | | arc4random_buf(out, size); |
82 | | return 1; |
83 | | #else |
84 | 0 | return 0; |
85 | 0 | #endif |
86 | 0 | } |
87 | 144 | return 1; |
88 | 144 | } |
89 | | #endif |
90 | | |
91 | | #if defined(MVM_random_use_getrandom_syscall) |
92 | | /* getrandom() was added to glibc much later than it was added to the kernel. Since |
93 | | * we detect the presence of the system call to decide whether to use this, |
94 | | * just use the syscall instead since the wrapper is not guaranteed to exist.*/ |
95 | | MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { |
96 | | long rtrn = syscall(SYS_getrandom, out, size, GRND_NONBLOCK); |
97 | | return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; |
98 | | } |
99 | | #elif defined(MVM_random_use_getrandom) |
100 | | /* Call the getrandom() wrapper in Solaris and FreeBSD since they were |
101 | | * added at the same time as getentropy() and this allows us to avoid blocking. */ |
102 | | MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { |
103 | | ssize_t rtrn = getrandom(out, size, GRND_NONBLOCK); |
104 | | return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; |
105 | | } |
106 | | |
107 | | #elif defined(MVM_random_use_getentropy) |
108 | | MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { |
109 | | int rtrn = getentropy(out, size); |
110 | | return rtrn <= 0 ? MVM_getrandom_urandom(tc, out, size) : 1; |
111 | | } |
112 | | |
113 | | #elif defined(_WIN32) |
114 | | #include <windows.h> |
115 | | #include <wincrypt.h> |
116 | | /* Signatures for pCryptAcquireContext() and pCryptGenRandom() */ |
117 | | typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,\ |
118 | | LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\ |
119 | | DWORD dwFlags ); |
120 | | typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,\ |
121 | | BYTE *pbBuffer ); |
122 | | /* The functions themselves */ |
123 | | static CRYPTGENRANDOM pCryptGenRandom = NULL; |
124 | | static HCRYPTPROV hCryptContext = 0; |
125 | | static int win32_urandom_init(void) { |
126 | | /* Get Module Handle to CryptoAPI */ |
127 | | HINSTANCE hAdvAPI32 = GetModuleHandle("advapi32.dll"); |
128 | | if (hAdvAPI32) { |
129 | | CRYPTACQUIRECONTEXTA pCryptAcquireContext = |
130 | | GetProcAddress(hAdvAPI32, "CryptAcquireContextA"); |
131 | | pCryptGenRandom = GetProcAddress(hAdvAPI32, "CryptGenRandom"); |
132 | | /* Check the pointers to the CryptoAPI functions. These shouldn't fail |
133 | | * but makes sure we won't have problems getting the context or getting |
134 | | * random. If those aren't NULL then get the pCrypt context */ |
135 | | return pCryptAcquireContext && pCryptGenRandom && |
136 | | pCryptAcquireContext(&hCryptContext, NULL, NULL, PROV_RSA_FULL, |
137 | | CRYPT_VERIFYCONTEXT) ? 1 : 0; |
138 | | } |
139 | | return 0; |
140 | | } |
141 | | MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { |
142 | | /* Return 0 if the context doesn't exist and we are unable to create it */ |
143 | | if (!hCryptContext && !win32_urandom_init()) |
144 | | return 0; |
145 | | return pCryptGenRandom(hCryptContext, (DWORD)size, (BYTE*)out) ? 1 : 0; |
146 | | } |
147 | | #else |
148 | 144 | MVMint32 MVM_getrandom (MVMThreadContext *tc, void *out, size_t size) { |
149 | 144 | return MVM_getrandom_urandom(tc, out, size); |
150 | 144 | } |
151 | | #endif |