mirror of
https://github.com/lua/lua.git
synced 2026-01-26 07:37:58 +00:00
Randomness added to table length computation
A bad actor could fill only a few entries in a table (power of twos in decreasing order, see tests) and produce a small table with a huge length. If your program builds a table with external data and iterates over its length, this behavior could be an issue.
This commit is contained in:
parent
ccb8b307f1
commit
303f415559
2
lapi.c
2
lapi.c
@ -440,7 +440,7 @@ LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
|
|||||||
case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen);
|
case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen);
|
||||||
case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen);
|
case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen);
|
||||||
case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len);
|
case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len);
|
||||||
case LUA_VTABLE: return luaH_getn(hvalue(o));
|
case LUA_VTABLE: return luaH_getn(L, hvalue(o));
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,8 @@
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Computes ceil(log2(x))
|
** Computes ceil(log2(x)), which is the smallest integer n such that
|
||||||
|
** x <= (1 << n).
|
||||||
*/
|
*/
|
||||||
lu_byte luaO_ceillog2 (unsigned int x) {
|
lu_byte luaO_ceillog2 (unsigned int x) {
|
||||||
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */
|
static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */
|
||||||
|
|||||||
50
ltable.c
50
ltable.c
@ -1220,24 +1220,36 @@ void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Try to find a boundary in the hash part of table 't'. From the
|
** Try to find a boundary in the hash part of table 't'. From the
|
||||||
** caller, we know that 'j' is zero or present and that 'j + 1' is
|
** caller, we know that 'asize + 1' is present. We want to find a larger
|
||||||
** present. We want to find a larger key that is absent from the
|
** key that is absent from the table, so that we can do a binary search
|
||||||
** table, so that we can do a binary search between the two keys to
|
** between the two keys to find a boundary. We keep doubling 'j' until
|
||||||
** find a boundary. We keep doubling 'j' until we get an absent index.
|
** we get an absent index. If the doubling would overflow, we try
|
||||||
** If the doubling would overflow, we try LUA_MAXINTEGER. If it is
|
** LUA_MAXINTEGER. If it is absent, we are ready for the binary search.
|
||||||
** absent, we are ready for the binary search. ('j', being max integer,
|
** ('j', being max integer, is larger or equal to 'i', but it cannot be
|
||||||
** is larger or equal to 'i', but it cannot be equal because it is
|
** equal because it is absent while 'i' is present.) Otherwise, 'j' is a
|
||||||
** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a
|
** boundary. ('j + 1' cannot be a present integer key because it is not
|
||||||
** boundary. ('j + 1' cannot be a present integer key because it is
|
** a valid integer in Lua.)
|
||||||
** not a valid integer in Lua.)
|
** About 'rnd': If we used a fixed algorithm, a bad actor could fill
|
||||||
|
** a table with only the keys that would be probed, in such a way that
|
||||||
|
** a small table could result in a huge length. To avoid that, we use
|
||||||
|
** the state's seed as a source of randomness. For the first probe,
|
||||||
|
** we "randomly double" 'i' by adding to it a random number roughly its
|
||||||
|
** width.
|
||||||
*/
|
*/
|
||||||
static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
|
static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) {
|
||||||
lua_Unsigned i;
|
lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */
|
||||||
if (j == 0) j++; /* the caller ensures 'j + 1' is present */
|
unsigned rnd = G(L)->seed;
|
||||||
do {
|
int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */
|
||||||
|
unsigned mask = (1u << n) - 1; /* 11...111 with the width of 'asize' */
|
||||||
|
unsigned incr = (rnd & mask) + 1; /* first increment (at least 1) */
|
||||||
|
lua_Unsigned j = (incr <= l_castS2U(LUA_MAXINTEGER) - i) ? i + incr : i + 1;
|
||||||
|
rnd >>= n; /* used 'n' bits from 'rnd' */
|
||||||
|
while (!hashkeyisempty(t, j)) { /* repeat until an absent t[j] */
|
||||||
i = j; /* 'i' is a present index */
|
i = j; /* 'i' is a present index */
|
||||||
if (j <= l_castS2U(LUA_MAXINTEGER) / 2)
|
if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) {
|
||||||
j *= 2;
|
j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */
|
||||||
|
rnd >>= 1;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
j = LUA_MAXINTEGER;
|
j = LUA_MAXINTEGER;
|
||||||
if (hashkeyisempty(t, j)) /* t[j] not present? */
|
if (hashkeyisempty(t, j)) /* t[j] not present? */
|
||||||
@ -1245,7 +1257,7 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
|
|||||||
else /* weird case */
|
else /* weird case */
|
||||||
return j; /* well, max integer is a boundary... */
|
return j; /* well, max integer is a boundary... */
|
||||||
}
|
}
|
||||||
} while (!hashkeyisempty(t, j)); /* repeat until an absent t[j] */
|
}
|
||||||
/* i < j && t[i] present && t[j] absent */
|
/* i < j && t[i] present && t[j] absent */
|
||||||
while (j - i > 1u) { /* do a binary search between them */
|
while (j - i > 1u) { /* do a binary search between them */
|
||||||
lua_Unsigned m = (i + j) / 2;
|
lua_Unsigned m = (i + j) / 2;
|
||||||
@ -1286,7 +1298,7 @@ static lua_Unsigned newhint (Table *t, unsigned hint) {
|
|||||||
** If there is no array part, or its last element is non empty, the
|
** If there is no array part, or its last element is non empty, the
|
||||||
** border may be in the hash part.
|
** border may be in the hash part.
|
||||||
*/
|
*/
|
||||||
lua_Unsigned luaH_getn (Table *t) {
|
lua_Unsigned luaH_getn (lua_State *L, Table *t) {
|
||||||
unsigned asize = t->asize;
|
unsigned asize = t->asize;
|
||||||
if (asize > 0) { /* is there an array part? */
|
if (asize > 0) { /* is there an array part? */
|
||||||
const unsigned maxvicinity = 4;
|
const unsigned maxvicinity = 4;
|
||||||
@ -1327,7 +1339,7 @@ lua_Unsigned luaH_getn (Table *t) {
|
|||||||
if (isdummy(t) || hashkeyisempty(t, asize + 1))
|
if (isdummy(t) || hashkeyisempty(t, asize + 1))
|
||||||
return asize; /* 'asize + 1' is empty */
|
return asize; /* 'asize + 1' is empty */
|
||||||
else /* 'asize + 1' is also non empty */
|
else /* 'asize + 1' is also non empty */
|
||||||
return hash_search(t, asize);
|
return hash_search(L, t, asize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
ltable.h
2
ltable.h
@ -173,7 +173,7 @@ LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize);
|
|||||||
LUAI_FUNC lu_mem luaH_size (Table *t);
|
LUAI_FUNC lu_mem luaH_size (Table *t);
|
||||||
LUAI_FUNC void luaH_free (lua_State *L, Table *t);
|
LUAI_FUNC void luaH_free (lua_State *L, Table *t);
|
||||||
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
|
LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
|
||||||
LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
|
LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t);
|
||||||
|
|
||||||
|
|
||||||
#if defined(LUA_DEBUG)
|
#if defined(LUA_DEBUG)
|
||||||
|
|||||||
2
lvm.c
2
lvm.c
@ -722,7 +722,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
|
|||||||
Table *h = hvalue(rb);
|
Table *h = hvalue(rb);
|
||||||
tm = fasttm(L, h->metatable, TM_LEN);
|
tm = fasttm(L, h->metatable, TM_LEN);
|
||||||
if (tm) break; /* metamethod? break switch to call it */
|
if (tm) break; /* metamethod? break switch to call it */
|
||||||
setivalue(s2v(ra), l_castU2S(luaH_getn(h))); /* else primitive len */
|
setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case LUA_VSHRSTR: {
|
case LUA_VSHRSTR: {
|
||||||
|
|||||||
@ -345,6 +345,18 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
do print("testing attack on table length")
|
||||||
|
local t = {}
|
||||||
|
local lim = math.floor(math.log(math.maxinteger, 2)) - 1
|
||||||
|
for i = lim, 0, -1 do
|
||||||
|
t[2^i] = true
|
||||||
|
end
|
||||||
|
assert(t[1 << lim])
|
||||||
|
-- next loop should not take forever
|
||||||
|
for i = 1, #t do end
|
||||||
|
end
|
||||||
|
|
||||||
local nofind = {}
|
local nofind = {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user