CVE-2018-11219: Integer Overflow or Wraparound (CWE-190)
value: An Integer Overflow issue was discovered in the struct library in the Lua subsystem in Redis before 3.2.12, 4.x before 4.0.10, and 5.x before 5.0 RC2, leading to a failure of bounds checking.
CWE description: The product performs a calculation that can
         produce an integer overflow or wraparound when the logic
         assumes that the resulting value will always be larger than
         the original value. This occurs when an integer value is
         incremented to a value that is too large to store in the
         associated representation. When this occurs, the value may
         become a very small or negative number.
CVSS v3 base score: 9.8
Published: 2018-06-17T17:29Z
Repository: https://github.com/antirez/redis
Language: C
Files changed: deps/lua/src/lua_struct.c
Commit date: 2018-06-13 12:40:33 +0200
Commit message: Security: update Lua struct package for security.

During an auditing Apple found that the "struct" Lua package
we ship with Redis (http://www.inf.puc-rio.br/~roberto/struct/) contains
a security problem. A bound-checking statement fails because of integer
overflow. The bug exists since we initially integrated this package with
Lua, when scripting was introduced, so every version of Redis with
EVAL/EVALSHA capabilities exposed is affected.

Instead of just fixing the bug, the library was updated to the latest
version shipped by the author.

Diff: diff --git a/deps/lua/src/lua_struct.c b/deps/lua/src/lua_struct.c
index a602bb430..b03a002da 100644
--- a/deps/lua/src/lua_struct.c
+++ b/deps/lua/src/lua_struct.c
@@ -1,9 +1,9 @@
 /*
 ** {======================================================
 ** Library for packing/unpacking structures.
-** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
+** $Id: struct.c,v 1.7 2018/05/11 22:04:31 roberto Exp $
 ** See Copyright Notice at the end of this file
 ** =======================================================
 */
 /*
 ** Valid formats:
@@ -13,12 +13,12 @@
 ** x - pading
 ** b/B - signed/unsigned byte
 ** h/H - signed/unsigned short
 ** l/L - signed/unsigned long
 ** T   - size_t
-** i/In - signed/unsigned integer with size `n' (default is size of int)
-** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
+** i/In - signed/unsigned integer with size 'n' (default is size of int)
+** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means
         the whole string; when unpacking, n==0 means use the previous
         read number as the string length
 ** s - zero-terminated string
 ** f - float
 ** d - double
@@ -87,18 +87,16 @@ typedef struct Header {
   int endian;
   int align;
 } Header;
 
 
-static int getnum (lua_State *L, const char **fmt, int df) {
+static int getnum (const char **fmt, int df) {
   if (!isdigit(**fmt))  /* no number? */
     return df;  /* return default value */
   else {
     int a = 0;
     do {
-      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
-        luaL_error(L, "integral size overflow");
       a = a*10 + *((*fmt)++) - '0';
     } while (isdigit(**fmt));
     return a;
   }
 }
@@ -115,13 +113,13 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
     case 'L': case 'l': return sizeof(long);
     case 'T': return sizeof(size_t);
     case 'f':  return sizeof(float);
     case 'd':  return sizeof(double);
     case 'x': return 1;
-    case 'c': return getnum(L, fmt, 1);
+    case 'c': return getnum(fmt, 1);
     case 'i': case 'I': {
-      int sz = getnum(L, fmt, sizeof(int));
+      int sz = getnum(fmt, sizeof(int));
       if (sz > MAXINTSIZE)
         luaL_error(L, "integral size %d is larger than limit of %d",
                        sz, MAXINTSIZE);
       return sz;
     }
@@ -150,11 +148,11 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
   switch (opt) {
     case  ' ': return;  /* ignore white spaces */
     case '>': h->endian = BIG; return;
     case '<': h->endian = LITTLE; return;
     case '!': {
-      int a = getnum(L, fmt, MAXALIGN);
+      int a = getnum(fmt, MAXALIGN);
       if (!isp2(a))
         luaL_error(L, "alignment %d is not a power of 2", a);
       h->align = a;
       return;
     }
@@ -294,68 +292,70 @@ static int b_unpack (lua_State *L) {
   Header h;
   const char *fmt = luaL_checkstring(L, 1);
   size_t ld;
   const char *data = luaL_checklstring(L, 2, &ld);
   size_t pos = luaL_optinteger(L, 3, 1) - 1;
+  int n = 0;  /* number of results */
   defaultoptions(&h);
-  lua_settop(L, 2);
   while (*fmt) {
     int opt = *fmt++;
     size_t size = optsize(L, opt, &fmt);
     pos += gettoalign(pos, &h, opt, size);
     luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
-    luaL_checkstack(L, 1, "too many results");
+    /* stack space for item + next position */
+    luaL_checkstack(L, 2, "too many results");
     switch (opt) {
       case 'b': case 'B': case 'h': case 'H':
       case 'l': case 'L': case 'T': case 'i':  case 'I': {  /* integer types */
         int issigned = islower(opt);
         lua_Number res = getinteger(data+pos, h.endian, issigned, size);
-        lua_pushnumber(L, res);
+        lua_pushnumber(L, res); n++;
         break;
       }
       case 'x': {
         break;
       }
       case 'f': {
         float f;
         memcpy(&f, data+pos, size);
         correctbytes((char *)&f, sizeof(f), h.endian);
-        lua_pushnumber(L, f);
+        lua_pushnumber(L, f); n++;
         break;
       }
       case 'd': {
         double d;
         memcpy(&d, data+pos, size);
         correctbytes((char *)&d, sizeof(d), h.endian);
-        lua_pushnumber(L, d);
+        lua_pushnumber(L, d); n++;
         break;
       }
       case 'c': {
         if (size == 0) {
-          if (!lua_isnumber(L, -1))
-            luaL_error(L, "format `c0' needs a previous size");
+          if (n == 0 || !lua_isnumber(L, -1))
+            luaL_error(L, "format 'c0' needs a previous size");
           size = lua_tonumber(L, -1);
-          lua_pop(L, 1);
-          luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
+          lua_pop(L, 1); n--;
+          luaL_argcheck(L, size <= ld && pos <= ld - size,
+                           2, "data string too short");
         }
-        lua_pushlstring(L, data+pos, size);
+        lua_pushlstring(L, data+pos, size); n++;
         break;
       }
       case 's': {
         const char *e = (const char *)memchr(data+pos, '\0', ld - pos);
         if (e == NULL)
           luaL_error(L, "unfinished string in data");
         size = (e - (data+pos)) + 1;
-        lua_pushlstring(L, data+pos, size - 1);
+        lua_pushlstring(L, data+pos, size - 1); n++;
         break;
       }
       default: controloptions(L, opt, &fmt, &h);
     }
     pos += size;
   }
-  lua_pushinteger(L, pos + 1);
-  return lua_gettop(L) - 2;
+  lua_pushinteger(L, pos + 1);  /* next position */
+  return n + 1;
 }
 
 
 static int b_size (lua_State *L) {
   Header h;
@@ -397,11 +397,11 @@ LUALIB_API int luaopen_struct (lua_State *L) {
   return 1;
 }
 
 
 /******************************************************************************
-* Copyright (C) 2010-2012 Lua.org, PUC-Rio.  All rights reserved.
+* Copyright (C) 2010-2018 Lua.org, PUC-Rio.  All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,

Vulnerable code: ** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
** i/In - signed/unsigned integer with size `n' (default is size of int)
** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
static int getnum (lua_State *L, const char **fmt, int df) {
      if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
        luaL_error(L, "integral size overflow");
    case 'c': return getnum(L, fmt, 1);
      int sz = getnum(L, fmt, sizeof(int));
      int a = getnum(L, fmt, MAXALIGN);
  lua_settop(L, 2);
    luaL_checkstack(L, 1, "too many results");
        lua_pushnumber(L, res);
        lua_pushnumber(L, f);
        lua_pushnumber(L, d);
          if (!lua_isnumber(L, -1))
            luaL_error(L, "format `c0' needs a previous size");
          lua_pop(L, 1);
          luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
        lua_pushlstring(L, data+pos, size);
        lua_pushlstring(L, data+pos, size - 1);
  lua_pushinteger(L, pos + 1);
  return lua_gettop(L) - 2;
* Copyright (C) 2010-2012 Lua.org, PUC-Rio.  All rights reserved.
Fixed code: ** $Id: struct.c,v 1.7 2018/05/11 22:04:31 roberto Exp $
** i/In - signed/unsigned integer with size 'n' (default is size of int)
** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means
static int getnum (const char **fmt, int df) {
    case 'c': return getnum(fmt, 1);
      int sz = getnum(fmt, sizeof(int));
      int a = getnum(fmt, MAXALIGN);
  int n = 0;  /* number of results */
    /* stack space for item + next position */
    luaL_checkstack(L, 2, "too many results");
        lua_pushnumber(L, res); n++;
        lua_pushnumber(L, f); n++;
        lua_pushnumber(L, d); n++;
          if (n == 0 || !lua_isnumber(L, -1))
            luaL_error(L, "format 'c0' needs a previous size");
          lua_pop(L, 1); n--;
          luaL_argcheck(L, size <= ld && pos <= ld - size,
                           2, "data string too short");
        lua_pushlstring(L, data+pos, size); n++;
        lua_pushlstring(L, data+pos, size - 1); n++;
  lua_pushinteger(L, pos + 1);  /* next position */
  return n + 1;
* Copyright (C) 2010-2018 Lua.org, PUC-Rio.  All rights reserved.