aboutsummaryrefslogtreecommitdiff
path: root/src/lj_prng.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_prng.c')
-rw-r--r--src/lj_prng.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/lj_prng.c b/src/lj_prng.c
new file mode 100644
index 00000000..62a6bbb7
--- /dev/null
+++ b/src/lj_prng.c
@@ -0,0 +1,225 @@
1/*
2** Pseudo-random number generation.
3** Copyright (C) 2005-2020 Mike Pall. See Copyright Notice in luajit.h
4*/
5
6#define lj_prng_c
7#define LUA_CORE
8
9/* To get the syscall prototype. */
10#if defined(__linux__) && !defined(_GNU_SOURCE)
11#define _GNU_SOURCE
12#endif
13
14#include "lj_def.h"
15#include "lj_arch.h"
16#include "lj_prng.h"
17
18/* -- PRNG step function -------------------------------------------------- */
19
20/* This implements a Tausworthe PRNG with period 2^223. Based on:
21** Tables of maximally-equidistributed combined LFSR generators,
22** Pierre L'Ecuyer, 1991, table 3, 1st entry.
23** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
24**
25** Important note: This PRNG is NOT suitable for cryptographic use!
26**
27** But it works fine for math.random(), which has an API that's not
28** suitable for cryptography, anyway.
29**
30** When used as a securely seeded global PRNG, it substantially raises
31** the difficulty for various attacks on the VM.
32*/
33
34/* Update generator i and compute a running xor of all states. */
35#define TW223_GEN(rs, z, r, i, k, q, s) \
36 z = rs->u[i]; \
37 z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
38 r ^= z; rs->u[i] = z;
39
40#define TW223_STEP(rs, z, r) \
41 TW223_GEN(rs, z, r, 0, 63, 31, 18) \
42 TW223_GEN(rs, z, r, 1, 58, 19, 28) \
43 TW223_GEN(rs, z, r, 2, 55, 24, 7) \
44 TW223_GEN(rs, z, r, 3, 47, 21, 8)
45
46/* PRNG step function with uint64_t result. */
47LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64(PRNGState *rs)
48{
49 uint64_t z, r = 0;
50 TW223_STEP(rs, z, r)
51 return r;
52}
53
54/* PRNG step function with double in uint64_t result. */
55LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64d(PRNGState *rs)
56{
57 uint64_t z, r = 0;
58 TW223_STEP(rs, z, r)
59 /* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
60 return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000);
61}
62
63/* Condition seed: ensure k[i] MSB of u[i] are non-zero. */
64static LJ_AINLINE void lj_prng_condition(PRNGState *rs)
65{
66 if (rs->u[0] < (1u << 1)) rs->u[0] += (1u << 1);
67 if (rs->u[1] < (1u << 6)) rs->u[1] += (1u << 6);
68 if (rs->u[2] < (1u << 9)) rs->u[2] += (1u << 9);
69 if (rs->u[3] < (1u << 17)) rs->u[3] += (1u << 17);
70}
71
72/* -- PRNG seeding from OS ------------------------------------------------ */
73
74#if LUAJIT_SECURITY_PRNG == 0
75
76/* Nothing to define. */
77
78#elif LJ_TARGET_XBOX360
79
80extern int XNetRandom(void *buf, unsigned int len);
81
82#elif LJ_TARGET_PS3
83
84extern int sys_get_random_number(void *buf, uint64_t len);
85
86#elif LJ_TARGET_PS4 || LJ_TARGET_PSVITA
87
88extern int sceRandomGetRandomNumber(void *buf, size_t len);
89
90#elif LJ_TARGET_WINDOWS || LJ_TARGET_XBOXONE
91
92#define WIN32_LEAN_AND_MEAN
93#include <windows.h>
94
95#if LJ_TARGET_UWP || LJ_TARGET_XBOXONE
96/* Must use BCryptGenRandom. */
97#include <bcrypt.h>
98#pragma comment(lib, "bcrypt.lib")
99#else
100/* If you wonder about this mess, then search online for RtlGenRandom. */
101typedef BOOLEAN (WINAPI *PRGR)(void *buf, ULONG len);
102static PRGR libfunc_rgr;
103#endif
104
105#elif LJ_TARGET_POSIX
106
107#if LJ_TARGET_LINUX
108/* Avoid a dependency on glibc 2.25+ and use the getrandom syscall instead. */
109#include <sys/syscall.h>
110#elif LJ_TARGET_OSX || LJ_TARGET_BSD || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN
111extern int getentropy(void *buf, size_t len);
112#ifdef __ELF__
113 __attribute__((weak))
114#endif
115;
116#endif
117
118/* For the /dev/urandom fallback. */
119#include <fcntl.h>
120#include <unistd.h>
121
122#endif
123
124#if LUAJIT_SECURITY_PRNG == 0
125
126/* If you really don't care about security, then define
127** LUAJIT_SECURITY_PRNG=0. This yields a predictable seed
128** and provides NO SECURITY against various attacks on the VM.
129**
130** BTW: This is NOT the way to get predictable table iteration,
131** predictable trace generation, predictable bytecode generation, etc.
132*/
133int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
134{
135 lj_prng_seed_fixed(rs); /* The fixed seed is already conditioned. */
136 return 1;
137}
138
139#else
140
141/* Securely seed PRNG from system entropy. Returns 0 on failure. */
142int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
143{
144#if LJ_TARGET_XBOX360
145
146 if (XNetRandom(rs->u, (unsigned int)sizeof(rs->u)) == 0)
147 goto ok;
148
149#elif LJ_TARGET_PS3
150
151 if (sys_get_random_number(rs->u, sizeof(rs->u)) == 0)
152 goto ok;
153
154#elif LJ_TARGET_PS4 || LJ_TARGET_PSVITA
155
156 if (sceRandomGetRandomNumber(rs->u, sizeof(rs->u) == 0)
157 goto ok;
158
159#elif LJ_TARGET_UWP || LJ_TARGET_XBOXONE
160
161 if (BCryptGenRandom(NULL, (PUCHAR)(rs->u), (ULONG)sizeof(rs->u),
162 BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0)
163 goto ok;
164
165#elif LJ_TARGET_WINDOWS
166
167 /* Keep the library loaded in case multiple VMs are started. */
168 if (!libfunc_rgr) {
169 HMODULE lib = LJ_WIN_LOADLIBA("advapi32.dll");
170 if (!lib) return 0;
171 libfunc_rgr = (PRGR)GetProcAddress(lib, "SystemFunction036");
172 if (!libfunc_rgr) return 0;
173 }
174 if (libfunc_rgr(rs->u, (ULONG)sizeof(rs->u)))
175 goto ok;
176
177#elif LJ_TARGET_POSIX
178
179#if LJ_TARGET_LINUX && defined(SYS_getrandom)
180
181 if (syscall(SYS_getrandom, rs->u, sizeof(rs->u), 0) == (long)sizeof(rs->u))
182 goto ok;
183
184#elif LJ_TARGET_OSX || LJ_TARGET_BSD || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN
185
186 if ((!__ELF__ || getentropy) && getentropy(rs->u, sizeof(rs->u)) == 0)
187 goto ok;
188
189#endif
190
191 /* Fallback to /dev/urandom. This may fail if the device is not
192 ** existent or accessible in a chroot or container, or if the process
193 ** or the OS ran out of file descriptors.
194 */
195 {
196 int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
197 if (fd != -1) {
198 ssize_t n = read(fd, rs->u, sizeof(rs->u));
199 (void)close(fd);
200 if (n == (ssize_t)sizeof(rs->u))
201 goto ok;
202 }
203 }
204
205#else
206
207 /* Add an elif above for your OS with a secure PRNG seed.
208 ** Note that fiddling around with rand(), getpid(), time() or coercing
209 ** ASLR to yield a few bits of randomness is not helpful.
210 ** If you don't want any security, then don't pretend you have any
211 ** and simply define LUAJIT_SECURITY_PRNG=0 for the build.
212 */
213#error "Missing secure PRNG seed for this OS"
214
215#endif
216 return 0; /* Fail. */
217
218ok:
219 lj_prng_condition(rs);
220 (void)lj_prng_u64(rs);
221 return 1; /* Success. */
222}
223
224#endif
225