New function 'luaL_makeseed'

This function unifies code from 'lua_newstate', 'math.randomseed',
and 'table.sort' that tries to create a value with a minimum level
of randomness.
This commit is contained in:
Roberto Ierusalimschy 2023-03-20 16:13:17 -03:00
parent 8c064fdc23
commit 5a04f1851e
9 changed files with 87 additions and 86 deletions

View File

@ -1091,8 +1091,56 @@ static void warnfon (void *ud, const char *message, int tocont) {
}
/*
** A function to compute an unsigned int with some level of
** randomness. Rely on Address Space Layout Randomization (if present),
** current time, and clock.
*/
#if !defined(luai_makeseed)
#include <time.h>
/*
** Size of 'e' measured in number of 'unsigned int's. (In the weird
** case that the division truncates, we just lose some part of the
** value, no big deal.)
*/
#define sof(e) (sizeof(e) / sizeof(unsigned int))
#define addbuff(b,v) \
(memcpy(b, &(v), sof(v) * sizeof(unsigned int)), b += sof(v))
static unsigned int luai_makeseed (void) {
unsigned int buff[sof(void*) + sof(clock_t) + sof(time_t)];
unsigned int res;
unsigned int *b = buff;
clock_t c = clock();
time_t t = time(NULL);
void *h = buff;
addbuff(b, h); /* local variable's address */
addbuff(b, c); /* clock */
addbuff(b, t); /* time */
res = buff[0];
for (b = buff + 1; b < buff + sof(buff); b++)
res += *b;
return res;
}
#endif
LUALIB_API unsigned int luaL_makeseed (lua_State *L) {
(void)L; /* unused */
return luai_makeseed();
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
lua_State *L = lua_newstate(l_alloc, NULL, luai_makeseed());
if (l_likely(L)) {
lua_atpanic(L, &panic);
lua_setwarnf(L, warnfoff, L); /* default is warnings off */

View File

@ -100,6 +100,8 @@ LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API unsigned int luaL_makeseed (lua_State *L);
LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,

View File

@ -603,28 +603,18 @@ static void setseed (lua_State *L, Rand64 *state,
}
/*
** Set a "random" seed. To get some randomness, use the current time
** and the address of 'L' (in case the machine does address space layout
** randomization).
*/
static void randseed (lua_State *L, RanState *state) {
lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
setseed(L, state->s, seed1, seed2);
}
static int math_randomseed (lua_State *L) {
RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
lua_Unsigned n1, n2;
if (lua_isnone(L, 1)) {
randseed(L, state);
n1 = luaL_makeseed(L);
n2 = I2UInt(state->s[0]);
}
else {
lua_Integer n1 = luaL_checkinteger(L, 1);
lua_Integer n2 = luaL_optinteger(L, 2, 0);
setseed(L, state->s, n1, n2);
n1 = luaL_checkinteger(L, 1);
n2 = luaL_optinteger(L, 2, 0);
}
setseed(L, state->s, n1, n2);
return 2; /* return seeds */
}
@ -641,7 +631,7 @@ static const luaL_Reg randfuncs[] = {
*/
static void setrandfunc (lua_State *L) {
RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
randseed(L, state); /* initialize with a "random" seed */
setseed(L, state->s, luaL_makeseed(L), 0); /* initialize with random seed */
lua_pop(L, 2); /* remove pushed seeds */
luaL_setfuncs(L, randfuncs, 1);
}

View File

@ -51,37 +51,6 @@ typedef struct LG {
#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
/*
** A macro to create a "random" seed when a state is created;
** the seed is used to randomize string hashes.
*/
#if !defined(luai_makeseed)
#include <time.h>
/*
** Compute an initial seed with some level of randomness.
** Rely on Address Space Layout Randomization (if present) and
** current time.
*/
#define addbuff(b,p,e) \
{ size_t t = cast_sizet(e); \
memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
static unsigned int luai_makeseed (lua_State *L) {
char buff[3 * sizeof(size_t)];
unsigned int h = cast_uint(time(NULL));
int p = 0;
addbuff(buff, p, L); /* heap variable */
addbuff(buff, p, &h); /* local variable */
addbuff(buff, p, &lua_newstate); /* public function */
lua_assert(p == sizeof(buff));
return luaS_hash(buff, p, h);
}
#endif
/*
** set GCdebt to a new value keeping the value (totalobjs + GCdebt)
** invariant (and avoiding underflows in 'totalobjs')
@ -350,7 +319,7 @@ LUA_API int lua_resetthread (lua_State *L, lua_State *from) {
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned int seed) {
int i;
lua_State *L;
global_State *g;
@ -370,7 +339,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
g->warnf = NULL;
g->ud_warn = NULL;
g->mainthread = L;
g->seed = luai_makeseed(L);
g->seed = seed;
g->gcstp = GCSTPGC; /* no GC while building state */
g->strt.size = g->strt.nuse = 0;
g->strt.hash = NULL;

View File

@ -230,31 +230,8 @@ typedef unsigned int IdxT;
** of a partition. (If you don't want/need this "randomness", ~0 is a
** good choice.)
*/
#if !defined(l_randomizePivot) /* { */
#include <time.h>
/* size of 'e' measured in number of 'unsigned int's */
#define sof(e) (sizeof(e) / sizeof(unsigned int))
/*
** Use 'time' and 'clock' as sources of "randomness". Because we don't
** know the types 'clock_t' and 'time_t', we cannot cast them to
** anything without risking overflows. A safe way to use their values
** is to copy them to an array of a known type and use the array values.
*/
static unsigned int l_randomizePivot (void) {
clock_t c = clock();
time_t t = time(NULL);
unsigned int buff[sof(c) + sof(t)];
unsigned int i, rnd = 0;
memcpy(buff, &c, sof(c) * sizeof(unsigned int));
memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
for (i = 0; i < sof(buff); i++)
rnd += buff[i];
return rnd;
}
#if !defined(l_randomizePivot)
#define l_randomizePivot(L) luaL_makeseed(L)
#endif /* } */
@ -391,7 +368,7 @@ static void auxsort (lua_State *L, IdxT lo, IdxT up,
up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
}
if ((up - lo) / 128 > n) /* partition too imbalanced? */
rnd = l_randomizePivot(); /* try a new randomization */
rnd = l_randomizePivot(L); /* try a new randomization */
} /* tail call auxsort(L, lo, up, rnd) */
}

View File

@ -1159,7 +1159,7 @@ static int num2int (lua_State *L) {
static int newstate (lua_State *L) {
void *ud;
lua_Alloc f = lua_getallocf(L, &ud);
lua_State *L1 = lua_newstate(f, ud);
lua_State *L1 = lua_newstate(f, ud, 0);
if (L1) {
lua_atpanic(L1, tpanic);
lua_pushlightuserdata(L, L1);
@ -1252,7 +1252,7 @@ static int checkpanic (lua_State *L) {
lua_Alloc f = lua_getallocf(L, &ud);
b.paniccode = luaL_optstring(L, 2, "");
b.L = L;
L1 = lua_newstate(f, ud); /* create new state */
L1 = lua_newstate(f, ud, 0); /* create new state */
if (L1 == NULL) { /* error? */
lua_pushnil(L);
return 1;

View File

@ -102,7 +102,8 @@ LUA_API void *debug_realloc (void *ud, void *block,
size_t osize, size_t nsize);
#if defined(lua_c)
#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol)
#define luaL_newstate() \
lua_newstate(debug_realloc, &l_memcontrol, luaL_makeseed(NULL))
#define luai_openlibs(L) \
{ luaL_openlibs(L); \
luaL_requiref(L, "T", luaB_opentests, 1); \

3
lua.h
View File

@ -160,7 +160,8 @@ extern const char lua_ident[];
/*
** state manipulation
*/
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud,
unsigned int seed);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API int (lua_resetthread) (lua_State *L, lua_State *from);

View File

@ -20,7 +20,7 @@ making it ideal for configuration, scripting,
and rapid prototyping.
Lua is implemented as a library, written in @emphx{clean C},
the common subset of @N{Standard C} and C++.
the common subset of C and C++.
The Lua distribution includes a host program called @id{lua},
which uses the Lua library to offer a complete,
standalone Lua interpreter,
@ -2957,7 +2957,7 @@ static void *l_alloc (void *ud, void *ptr, size_t osize,
return realloc(ptr, nsize);
}
}
Note that @N{Standard C} ensures
Note that @N{ISO C} ensures
that @T{free(NULL)} has no effect and that
@T{realloc(NULL,size)} is equivalent to @T{malloc(size)}.
@ -3644,7 +3644,8 @@ Other upvalues are initialized with @nil.
}
@APIEntry{lua_State *lua_newstate (lua_Alloc f, void *ud);|
@APIEntry{lua_State *lua_newstate (lua_Alloc f, void *ud,
unsigned int seed);|
@apii{0,0,-}
Creates a new independent state and returns its main thread.
@ -3655,6 +3656,8 @@ Lua will do all memory allocation for this state
through this function @seeF{lua_Alloc}.
The second argument, @id{ud}, is an opaque pointer that Lua
passes to the allocator in every call.
The third argument, @id{seed}, is a seed for the hashing of
strings when they are used as table keys.
}
@ -5721,6 +5724,16 @@ it does not run it.
}
@APIEntry{unsigned int luaL_makeseed (lua_State *L);|
@apii{0,0,-}
Returns a value with a weak attempt for randomness.
(It produces that value based on the current date and time,
the current processor time, and the address of an internal variable,
in case the machine has Address Space Layout Randomization.)
}
@APIEntry{void luaL_newlib (lua_State *L, const luaL_Reg l[]);|
@apii{0,1,m}
@ -6892,7 +6905,7 @@ including if necessary a path and an extension.
@id{funcname} must be the exact name exported by the @N{C library}
(which may depend on the @N{C compiler} and linker used).
This function is not supported by @N{Standard C}.
This function is not supported by @N{ISO C}.
As such, it is only available on some platforms
(Windows, Linux, Mac OS X, Solaris, BSD,
plus other Unix systems that support the @id{dlfcn} standard).
@ -8093,7 +8106,7 @@ different sequences of results each time the program runs.
When called with at least one argument,
the integer parameters @id{x} and @id{y} are
joined into a 128-bit @emphx{seed} that
joined into a @emphx{seed} that
is used to reinitialize the pseudo-random generator;
equal seeds produce equal sequences of numbers.
The default for @id{y} is zero.