Removed 'int' size limit for pack/unpack

This commit is contained in:
Roberto Ierusalimschy 2024-06-21 14:55:12 -03:00
parent e24ce8c2b3
commit ec65ab878e
2 changed files with 48 additions and 38 deletions

View File

@ -1447,14 +1447,14 @@ typedef enum KOption {
*/ */
static int digit (int c) { return '0' <= c && c <= '9'; } static int digit (int c) { return '0' <= c && c <= '9'; }
static int getnum (const char **fmt, int df) { static size_t getnum (const char **fmt, size_t df) {
if (!digit(**fmt)) /* no number? */ if (!digit(**fmt)) /* no number? */
return df; /* return default value */ return df; /* return default value */
else { else {
int a = 0; size_t a = 0;
do { do {
a = a*10 + (*((*fmt)++) - '0'); a = a*10 + (*((*fmt)++) - '0');
} while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); } while (digit(**fmt) && a <= (MAX_SIZE - 9)/10);
return a; return a;
} }
} }
@ -1462,14 +1462,14 @@ static int getnum (const char **fmt, int df) {
/* /*
** Read an integer numeral and raises an error if it is larger ** Read an integer numeral and raises an error if it is larger
** than the maximum size for integers. ** than the maximum size of integers.
*/ */
static int getnumlimit (Header *h, const char **fmt, int df) { static int getnumlimit (Header *h, const char **fmt, int df) {
int sz = getnum(fmt, df); size_t sz = getnum(fmt, df);
if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) if (l_unlikely((sz - 1u) >= MAXINTSIZE))
return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
sz, MAXINTSIZE); sz, MAXINTSIZE);
return sz; return cast_int(sz);
} }
@ -1486,7 +1486,7 @@ static void initheader (lua_State *L, Header *h) {
/* /*
** Read and classify next option. 'size' is filled with option's size. ** Read and classify next option. 'size' is filled with option's size.
*/ */
static KOption getoption (Header *h, const char **fmt, int *size) { static KOption getoption (Header *h, const char **fmt, size_t *size) {
/* dummy structure to get native alignment requirements */ /* dummy structure to get native alignment requirements */
struct cD { char c; union { LUAI_MAXALIGN; } u; }; struct cD { char c; union { LUAI_MAXALIGN; } u; };
int opt = *((*fmt)++); int opt = *((*fmt)++);
@ -1508,8 +1508,8 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
case 'c': case 'c':
*size = getnum(fmt, -1); *size = getnum(fmt, cast_sizet(-1));
if (l_unlikely(*size == -1)) if (l_unlikely(*size == cast_sizet(-1)))
luaL_error(h->L, "missing size for format option 'c'"); luaL_error(h->L, "missing size for format option 'c'");
return Kchar; return Kchar;
case 'z': return Kzstr; case 'z': return Kzstr;
@ -1540,9 +1540,9 @@ static KOption getoption (Header *h, const char **fmt, int *size) {
** despite its size. ** despite its size.
*/ */
static KOption getdetails (Header *h, size_t totalsize, static KOption getdetails (Header *h, size_t totalsize,
const char **fmt, int *psize, int *ntoalign) { const char **fmt, size_t *psize, int *ntoalign) {
KOption opt = getoption(h, fmt, psize); KOption opt = getoption(h, fmt, psize);
int align = *psize; /* usually, alignment follows size */ size_t align = *psize; /* usually, alignment follows size */
if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
luaL_argerror(h->L, 1, "invalid next option for option 'X'"); luaL_argerror(h->L, 1, "invalid next option for option 'X'");
@ -1550,9 +1550,9 @@ static KOption getdetails (Header *h, size_t totalsize,
if (align <= 1 || opt == Kchar) /* need no alignment? */ if (align <= 1 || opt == Kchar) /* need no alignment? */
*ntoalign = 0; *ntoalign = 0;
else { else {
if (align > h->maxalign) /* enforce maximum alignment */ if (align > cast_sizet(h->maxalign)) /* enforce maximum alignment */
align = h->maxalign; align = h->maxalign;
if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ if (l_unlikely(!ispow2(align))) /* not a power of 2? */
luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
} }
@ -1609,8 +1609,11 @@ static int str_pack (lua_State *L) {
lua_pushnil(L); /* mark to separate arguments from string buffer */ lua_pushnil(L); /* mark to separate arguments from string buffer */
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
while (*fmt != '\0') { while (*fmt != '\0') {
int size, ntoalign; int ntoalign;
size_t size;
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
luaL_argcheck(L, size + ntoalign <= MAX_SIZE - totalsize, arg,
"result too long");
totalsize += ntoalign + size; totalsize += ntoalign + size;
while (ntoalign-- > 0) while (ntoalign-- > 0)
luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */
@ -1660,18 +1663,21 @@ static int str_pack (lua_State *L) {
case Kchar: { /* fixed-size string */ case Kchar: { /* fixed-size string */
size_t len; size_t len;
const char *s = luaL_checklstring(L, arg, &len); const char *s = luaL_checklstring(L, arg, &len);
luaL_argcheck(L, len <= (size_t)size, arg, luaL_argcheck(L, len <= size, arg, "string longer than given size");
"string longer than given size");
luaL_addlstring(&b, s, len); /* add string */ luaL_addlstring(&b, s, len); /* add string */
while (len++ < (size_t)size) /* pad extra space */ if (len < size) { /* does it need padding? */
luaL_addchar(&b, LUAL_PACKPADBYTE); size_t psize = size - len; /* pad size */
char *buff = luaL_prepbuffsize(&b, psize);
memset(buff, LUAL_PACKPADBYTE, psize);
luaL_addsize(&b, psize);
}
break; break;
} }
case Kstring: { /* strings with length count */ case Kstring: { /* strings with length count */
size_t len; size_t len;
const char *s = luaL_checklstring(L, arg, &len); const char *s = luaL_checklstring(L, arg, &len);
luaL_argcheck(L, size >= (int)sizeof(size_t) || luaL_argcheck(L, size >= sizeof(lua_Unsigned) ||
len < ((size_t)1 << (size * NB)), len < ((lua_Unsigned)1 << (size * NB)),
arg, "string length does not fit in given size"); arg, "string length does not fit in given size");
packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
luaL_addlstring(&b, s, len); luaL_addlstring(&b, s, len);
@ -1701,19 +1707,20 @@ static int str_pack (lua_State *L) {
static int str_packsize (lua_State *L) { static int str_packsize (lua_State *L) {
Header h; Header h;
const char *fmt = luaL_checkstring(L, 1); /* format string */ const char *fmt = luaL_checkstring(L, 1); /* format string */
size_t totalsize = 0; /* accumulate total size of result */ lua_Integer totalsize = 0; /* accumulate total size of result */
initheader(L, &h); initheader(L, &h);
while (*fmt != '\0') { while (*fmt != '\0') {
int size, ntoalign; int ntoalign;
size_t size;
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
"variable-length format"); "variable-length format");
size += ntoalign; /* total space used by option */ size += ntoalign; /* total space used by option */
luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - cast(lua_Integer, size),
"format result too large"); 1, "format result too large");
totalsize += size; totalsize += size;
} }
lua_pushinteger(L, (lua_Integer)totalsize); lua_pushinteger(L, totalsize);
return 1; return 1;
} }
@ -1762,9 +1769,10 @@ static int str_unpack (lua_State *L) {
luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
initheader(L, &h); initheader(L, &h);
while (*fmt != '\0') { while (*fmt != '\0') {
int size, ntoalign; int ntoalign;
size_t size;
KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, luaL_argcheck(L, ntoalign + size <= ld - pos, 2,
"data string too short"); "data string too short");
pos += ntoalign; /* skip alignment */ pos += ntoalign; /* skip alignment */
/* stack space for item + next position */ /* stack space for item + next position */
@ -1801,7 +1809,8 @@ static int str_unpack (lua_State *L) {
break; break;
} }
case Kstring: { case Kstring: {
size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos,
h.islittle, size, 0);
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
lua_pushlstring(L, data + pos + size, len); lua_pushlstring(L, data + pos + size, len);
pos += len; /* skip string */ pos += len; /* skip string */

View File

@ -135,15 +135,15 @@ checkerror("variable%-length format", packsize, "z")
-- overflow in option size (error will be in digit after limit) -- overflow in option size (error will be in digit after limit)
checkerror("invalid format", packsize, "c1" .. string.rep("0", 40)) checkerror("invalid format", packsize, "c1" .. string.rep("0", 40))
if packsize("i") == 4 then do
-- result would be 2^31 (2^3 repetitions of 2^28 strings) local maxsize = (packsize("j") <= packsize("T")) and
local s = string.rep("c268435456", 2^3) math.maxinteger or (1 << (packsize("T") * 8))
checkerror("too large", packsize, s) assert (packsize(string.format("c%d", maxsize - 9)) == maxsize - 9)
-- one less is OK checkerror("too large", packsize, string.format("c%dc10", maxsize - 9))
s = string.rep("c268435456", 2^3 - 1) .. "c268435455" checkerror("too long", pack, string.format("xxxxxxxxxx c%d", maxsize - 9))
assert(packsize(s) == 0x7fffffff)
end end
-- overflow in packing -- overflow in packing
for i = 1, sizeLI - 1 do for i = 1, sizeLI - 1 do
local umax = (1 << (i * 8)) - 1 local umax = (1 << (i * 8)) - 1
@ -229,8 +229,9 @@ do
assert(pack("c3", "123") == "123") assert(pack("c3", "123") == "123")
assert(pack("c0", "") == "") assert(pack("c0", "") == "")
assert(pack("c8", "123456") == "123456\0\0") assert(pack("c8", "123456") == "123456\0\0")
assert(pack("c88", "") == string.rep("\0", 88)) assert(pack("c88 c1", "", "X") == string.rep("\0", 88) .. "X")
assert(pack("c188", "ab") == "ab" .. string.rep("\0", 188 - 2)) assert(pack("c188 c2", "ab", "X\1") ==
"ab" .. string.rep("\0", 188 - 2) .. "X\1")
local a, b, c = unpack("!4 z c3", "abcdefghi\0xyz") local a, b, c = unpack("!4 z c3", "abcdefghi\0xyz")
assert(a == "abcdefghi" and b == "xyz" and c == 14) assert(a == "abcdefghi" and b == "xyz" and c == 14)
checkerror("longer than", pack, "c3", "1234") checkerror("longer than", pack, "c3", "1234")