Coverage Report

Created: 2018-07-03 15:31

/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