Browse Source

Removed 'int' size limit for pack/unpack

Roberto Ierusalimschy 1 year ago
parent
commit
ec65ab878e
2 changed files with 48 additions and 38 deletions
  1. 38 29
      lstrlib.c
  2. 10 9
      testes/tpack.lua

+ 38 - 29
lstrlib.c

@@ -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);
-  if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
+  size_t sz = getnum(fmt, df);
+  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);
-      if (l_unlikely(*size == -1))
+      *size = getnum(fmt, cast_sizet(-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,
-                         "string longer than given size");
+        luaL_argcheck(L, len <= size, arg, "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 */
-          luaL_addchar(&b, LUAL_PACKPADBYTE);
+        if (len < size) {  /* does it need padding? */
+          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) ||
-                         len < ((size_t)1 << (size * NB)),
+        luaL_argcheck(L, size >= sizeof(lua_Unsigned) ||
+                         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,
-                     "format result too large");
+    luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - cast(lua_Integer, size),
+                     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 */

+ 10 - 9
testes/tpack.lua

@@ -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
-  -- result would be 2^31  (2^3 repetitions of 2^28 strings)
-  local s = string.rep("c268435456", 2^3)
-  checkerror("too large", packsize, s)
-  -- one less is OK
-  s = string.rep("c268435456", 2^3 - 1) .. "c268435455"
-  assert(packsize(s) == 0x7fffffff)
+do
+  local maxsize = (packsize("j") <= packsize("T")) and
+                      math.maxinteger or (1 << (packsize("T") * 8))
+  assert (packsize(string.format("c%d", maxsize - 9)) == maxsize - 9)
+  checkerror("too large", packsize, string.format("c%dc10", maxsize - 9))
+  checkerror("too long", pack, string.format("xxxxxxxxxx c%d", maxsize - 9))
 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("c188", "ab") == "ab" .. string.rep("\0", 188 - 2))
+  assert(pack("c88 c1", "", "X") == string.rep("\0", 88) .. "X")
+  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")