summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/luajit-3065c9/src/lj_prng.c
blob: 9e57505e8102794ac49761c58a1e17fd39b5e59a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
** Pseudo-random number generation.
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
*/

#define lj_prng_c
#define LUA_CORE

/* To get the syscall prototype. */
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif

#include "lj_def.h"
#include "lj_arch.h"
#include "lj_prng.h"

/* -- PRNG step function -------------------------------------------------- */

/* This implements a Tausworthe PRNG with period 2^223. Based on:
**   Tables of maximally-equidistributed combined LFSR generators,
**   Pierre L'Ecuyer, 1991, table 3, 1st entry.
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
**
** Important note: This PRNG is NOT suitable for cryptographic use!
**
** But it works fine for math.random(), which has an API that's not
** suitable for cryptography, anyway.
**
** When used as a securely seeded global PRNG, it substantially raises
** the difficulty for various attacks on the VM.
*/

/* Update generator i and compute a running xor of all states. */
#define TW223_GEN(rs, z, r, i, k, q, s) \
  z = rs->u[i]; \
  z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
  r ^= z; rs->u[i] = z;

#define TW223_STEP(rs, z, r) \
  TW223_GEN(rs, z, r, 0, 63, 31, 18) \
  TW223_GEN(rs, z, r, 1, 58, 19, 28) \
  TW223_GEN(rs, z, r, 2, 55, 24,  7) \
  TW223_GEN(rs, z, r, 3, 47, 21,  8)

/* PRNG step function with uint64_t result. */
LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64(PRNGState *rs)
{
  uint64_t z, r = 0;
  TW223_STEP(rs, z, r)
  return r;
}

/* PRNG step function with double in uint64_t result. */
LJ_NOINLINE uint64_t LJ_FASTCALL lj_prng_u64d(PRNGState *rs)
{
  uint64_t z, r = 0;
  TW223_STEP(rs, z, r)
  /* Returns a double bit pattern in the range 1.0 <= d < 2.0. */
  return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000);
}

/* Condition seed: ensure k[i] MSB of u[i] are non-zero. */
static LJ_AINLINE void lj_prng_condition(PRNGState *rs)
{
  if (rs->u[0] < (1u << 1)) rs->u[0] += (1u << 1);
  if (rs->u[1] < (1u << 6)) rs->u[1] += (1u << 6);
  if (rs->u[2] < (1u << 9)) rs->u[2] += (1u << 9);
  if (rs->u[3] < (1u << 17)) rs->u[3] += (1u << 17);
}

/* -- PRNG seeding from OS ------------------------------------------------ */

#if LUAJIT_SECURITY_PRNG == 0

/* Nothing to define. */

#elif LJ_TARGET_XBOX360

extern int XNetRandom(void *buf, unsigned int len);

#elif LJ_TARGET_PS3

extern int sys_get_random_number(void *buf, uint64_t len);

#elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA

extern int sceRandomGetRandomNumber(void *buf, size_t len);

#elif LJ_TARGET_NX

#include <unistd.h>

#elif LJ_TARGET_WINDOWS || LJ_TARGET_XBOXONE

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#if LJ_TARGET_UWP || LJ_TARGET_XBOXONE
/* Must use BCryptGenRandom. */
#include <bcrypt.h>
#pragma comment(lib, "bcrypt.lib")
#else
/* If you wonder about this mess, then search online for RtlGenRandom. */
typedef BOOLEAN (WINAPI *PRGR)(void *buf, ULONG len);
static PRGR libfunc_rgr;
#endif

#elif LJ_TARGET_POSIX

#if LJ_TARGET_LINUX
/* Avoid a dependency on glibc 2.25+ and use the getrandom syscall instead. */
#include <sys/syscall.h>
#else

#if LJ_TARGET_OSX && !LJ_TARGET_IOS
/*
** In their infinite wisdom Apple decided to disallow getentropy() in the
** iOS App Store. Even though the call is common to all BSD-ish OS, it's
** recommended by Apple in their own security-related docs, and, to top
** off the foolery, /dev/urandom is handled by the same kernel code,
** yet accessing it is actually permitted (but less efficient).
*/
#include <Availability.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
#define LJ_TARGET_HAS_GETENTROPY	1
#endif
#elif (LJ_TARGET_BSD && !defined(__NetBSD__)) || LJ_TARGET_SOLARIS || LJ_TARGET_CYGWIN
#define LJ_TARGET_HAS_GETENTROPY	1
#endif

#if LJ_TARGET_HAS_GETENTROPY
extern int getentropy(void *buf, size_t len)
#ifdef __ELF__
  __attribute__((weak))
#endif
;
#endif

#endif

/* For the /dev/urandom fallback. */
#include <fcntl.h>
#include <unistd.h>

#endif

#if LUAJIT_SECURITY_PRNG == 0

/* If you really don't care about security, then define
** LUAJIT_SECURITY_PRNG=0. This yields a predictable seed
** and provides NO SECURITY against various attacks on the VM.
**
** BTW: This is NOT the way to get predictable table iteration,
** predictable trace generation, predictable bytecode generation, etc.
*/
int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
{
  lj_prng_seed_fixed(rs);  /* The fixed seed is already conditioned. */
  return 1;
}

#else

/* Securely seed PRNG from system entropy. Returns 0 on failure. */
int LJ_FASTCALL lj_prng_seed_secure(PRNGState *rs)
{
#if LJ_TARGET_XBOX360

  if (XNetRandom(rs->u, (unsigned int)sizeof(rs->u)) == 0)
    goto ok;

#elif LJ_TARGET_PS3

  if (sys_get_random_number(rs->u, sizeof(rs->u)) == 0)
    goto ok;

#elif LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA

  if (sceRandomGetRandomNumber(rs->u, sizeof(rs->u)) == 0)
    goto ok;

#elif LJ_TARGET_NX

  if (getentropy(rs->u, sizeof(rs->u)) == 0)
    goto ok;

#elif LJ_TARGET_UWP || LJ_TARGET_XBOXONE

  if (BCryptGenRandom(NULL, (PUCHAR)(rs->u), (ULONG)sizeof(rs->u),
		      BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0)
    goto ok;

#elif LJ_TARGET_WINDOWS

  /* Keep the library loaded in case multiple VMs are started. */
  if (!libfunc_rgr) {
    HMODULE lib = LJ_WIN_LOADLIBA("advapi32.dll");
    if (!lib) return 0;
    libfunc_rgr = (PRGR)GetProcAddress(lib, "SystemFunction036");
    if (!libfunc_rgr) return 0;
  }
  if (libfunc_rgr(rs->u, (ULONG)sizeof(rs->u)))
    goto ok;

#elif LJ_TARGET_POSIX

#if LJ_TARGET_LINUX && defined(SYS_getrandom)

  if (syscall(SYS_getrandom, rs->u, sizeof(rs->u), 0) == (long)sizeof(rs->u))
    goto ok;

#elif LJ_TARGET_HAS_GETENTROPY

#ifdef __ELF__
  if (&getentropy && getentropy(rs->u, sizeof(rs->u)) == 0)
    goto ok;
#else
  if (getentropy(rs->u, sizeof(rs->u)) == 0)
    goto ok;
#endif

#endif

  /* Fallback to /dev/urandom. This may fail if the device is not
  ** existent or accessible in a chroot or container, or if the process
  ** or the OS ran out of file descriptors.
  */
  {
    int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC);
    if (fd != -1) {
      ssize_t n = read(fd, rs->u, sizeof(rs->u));
      (void)close(fd);
      if (n == (ssize_t)sizeof(rs->u))
	goto ok;
    }
  }

#else

  /* Add an elif above for your OS with a secure PRNG seed.
  ** Note that fiddling around with rand(), getpid(), time() or coercing
  ** ASLR to yield a few bits of randomness is not helpful.
  ** If you don't want any security, then don't pretend you have any
  ** and simply define LUAJIT_SECURITY_PRNG=0 for the build.
  */
#error "Missing secure PRNG seed for this OS"

#endif
  return 0;  /* Fail. */

ok:
  lj_prng_condition(rs);
  (void)lj_prng_u64(rs);
  return 1;  /* Success. */
}

#endif