Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/lstack.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ class LuaStack {

/* Convert stack pointer to offset from base */
inline ptrdiff_t save(StkId pt) const noexcept {
return cast_charp(pt) - cast_charp(stack.p);
return pt - stack.p; /* direct pointer arithmetic, no char* round-trip */
}

/* Convert offset to stack pointer */
inline StkId restore(ptrdiff_t n) const noexcept {
return reinterpret_cast<StkId>(cast_charp(stack.p) + n);
return stack.p + n; /* direct pointer arithmetic, safe with LTO */
}

/*
Expand Down
20 changes: 19 additions & 1 deletion src/objects/ltable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <algorithm>
#include <cmath>
#include <climits>
#include <cstdint>
#include <cstring>

#include "lua.h"
Expand Down Expand Up @@ -109,8 +110,15 @@ class NodeArray {
if (withLastfree) {
// Large table: allocate Limbox + Node[]
// LAYOUT: [Limbox header][Node array of size n]
// Verify no overflow in size calculation
if (n > (MAX_SIZET - sizeof(Limbox)) / sizeof(Node)) {
luaG_runerror(L, "table size overflow");
}
size_t total = sizeof(Limbox) + n * sizeof(Node);
char* block = luaM_newblock(L, total);
// Verify alignment assumptions (critical for type punning safety)
lua_assert(reinterpret_cast<uintptr_t>(block) % alignof(Limbox) == 0);
lua_assert(reinterpret_cast<uintptr_t>(block + sizeof(Limbox)) % alignof(Node) == 0);
// Limbox is at the start, nodes follow
// Safe per C++17 §8.2.10: reinterpret_cast to properly aligned type
Limbox* limbox = reinterpret_cast<Limbox*>(block);
Expand All @@ -131,6 +139,8 @@ class NodeArray {
// nodeStart points to element after Limbox, so (nodeStart - 1) conceptually
// points to the Limbox (treating the block as Limbox array for arithmetic purposes)
Limbox* limbox = reinterpret_cast<Limbox*>(nodeStart) - 1;
// Verify we're not accessing uninitialized memory
lua_assert(limbox->lastfree >= nodeStart);
return limbox->lastfree;
}
};
Expand Down Expand Up @@ -704,6 +714,9 @@ static Value *resizearray (lua_State *L , Table *t,
unsigned tomove = (oldasize < newasize) ? oldasize : newasize;
size_t tomoveb = (oldasize < newasize) ? oldasizeb : newasizeb;
lua_assert(tomoveb > 0);
lua_assert(tomove <= newasize); /* ensure destination bounds */
lua_assert(tomove <= oldasize); /* ensure source bounds */
lua_assert(tomoveb <= newasizeb); /* verify size calculation */
memcpy(np - tomove, op - tomove, tomoveb);
luaM_freemem(L, op - oldasize, oldasizeb); /* free old block */
}
Expand All @@ -727,7 +740,9 @@ static void setnodevector (lua_State *L, Table *t, unsigned size) {
}
else {
unsigned int lsize = luaO_ceillog2(size);
if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE)
if (lsize > MAXHBITS)
luaG_runerror(L, "table overflow");
if ((1u << lsize) > MAXHSIZE)
luaG_runerror(L, "table overflow");
size = Table::powerOfTwo(lsize);
bool needsLastfree = (lsize >= LIMFORLAST);
Expand Down Expand Up @@ -1240,14 +1255,17 @@ static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) {
lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */
unsigned rnd = G(L)->getSeed();
int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */
lua_assert(n >= 0 && n < 32); /* ensure shift is safe (avoid UB) */
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 */
if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) {
lua_Unsigned old_j = j;
j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */
lua_assert(j > old_j && j <= l_castS2U(LUA_MAXINTEGER)); /* no wrap */
rnd >>= 1;
}
else {
Expand Down
11 changes: 9 additions & 2 deletions src/vm/lvm_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,14 @@ void luaV_concat (lua_State *L, int total) {
StkId top = L->getTop().p;
int n = 2; /* number of elements handled in this pass (at least 2) */
if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
!tostring(L, s2v(top - 1)))
!tostring(L, s2v(top - 1))) {
luaT_tryconcatTM(L); /* may invalidate 'top' */
else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
top = L->getTop().p; /* recapture after potential GC */
}
else if (isemptystr(s2v(top - 1))) { /* second operand is empty? */
cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
top = L->getTop().p; /* recapture after potential GC */
}
else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
*s2v(top - 2) = *s2v(top - 1); /* result is second op. (operator=) */
}
Expand All @@ -71,6 +75,7 @@ void luaV_concat (lua_State *L, int total) {
TString *ts;
/* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
top = L->getTop().p; /* recapture after tostring() which can trigger GC */
size_t l = tsslen(tsvalue(s2v(top - n - 1)));
if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) {
L->getStackSubsystem().setTopPtr(top - total); /* pop strings to avoid wasting stack */
Expand All @@ -82,9 +87,11 @@ void luaV_concat (lua_State *L, int total) {
char buff[LUAI_MAXSHORTLEN];
copy2buff(top, n, buff); /* copy strings to buffer */
ts = luaS_newlstr(L, buff, tl);
top = L->getTop().p; /* recapture after potential GC */
}
else { /* long string; copy strings directly to final result */
ts = luaS_createlngstrobj(L, tl);
top = L->getTop().p; /* recapture after potential GC */
copy2buff(top, n, getlngstr(ts));
}
setsvalue2s(L, top - n, ts); /* create result */
Expand Down
Loading