luci-lib-jsonc: prevent infinite recursion in stringify
authorJo-Philipp Wich <jo@mein.io>
Fri, 21 Dec 2018 12:16:16 +0000 (13:16 +0100)
committerJo-Philipp Wich <jo@mein.io>
Fri, 21 Dec 2018 14:41:19 +0000 (15:41 +0100)
Also increase the stack size as needed to prevent crashes when serializing
deeply nested tables.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
libs/luci-lib-jsonc/src/jsonc.c

index ef1110166055a78bf32cf1a6fbbd3e356b2bce3f..9ff8520dbc43a2c7958b0882395ef15997254f33 100644 (file)
@@ -27,6 +27,12 @@ limitations under the License.
 #define LUCI_JSONC "luci.jsonc"
 #define LUCI_JSONC_PARSER "luci.jsonc.parser"
 
+struct seen {
+       size_t size;
+       size_t len;
+       const void *ptrs[];
+};
+
 struct json_state {
        struct json_object *obj;
        struct json_tokener *tok;
@@ -35,6 +41,7 @@ struct json_state {
 
 static void _json_to_lua(lua_State *L, struct json_object *obj);
 static struct json_object * _lua_to_json(lua_State *L, int index);
+static struct json_object * _lua_to_json_rec(lua_State *L, int index, struct seen **seen);
 
 static int json_new(lua_State *L)
 {
@@ -199,6 +206,9 @@ static int _lua_test_array(lua_State *L, int index)
        int max = 0;
        lua_Number idx;
 
+       if (!lua_checkstack(L, 2))
+               return -1;
+
        lua_pushnil(L);
 
        /* check for non-integer keys */
@@ -243,16 +253,54 @@ out:
        return max;
 }
 
-static struct json_object * _lua_to_json(lua_State *L, int index)
+
+static bool visited(struct seen **sp, const void *ptr) {
+       struct seen *s = *sp;
+       size_t i;
+
+       if (s->len >= s->size)
+       {
+               i = s->size + 10;
+               s = realloc(*sp, sizeof(struct seen) + sizeof(void *) * i);
+
+               if (!s)
+               {
+                       if (*sp)
+                               free(*sp);
+
+                       *sp = NULL;
+                       return true;
+               }
+
+               s->size = i;
+               *sp = s;
+       }
+
+       for (i = 0; i < s->len; i++)
+               if (s->ptrs[i] == ptr)
+                       return true;
+
+       s->ptrs[s->len++] = ptr;
+       return false;
+}
+
+static struct json_object * _lua_to_json_rec(lua_State *L, int index,
+                                             struct seen **seen)
 {
        lua_Number nd, ni;
        struct json_object *obj;
        const char *key;
        int i, max;
 
+       if (index < 0)
+               index = lua_gettop(L) + index + 1;
+
        switch (lua_type(L, index))
        {
        case LUA_TTABLE:
+               if (visited(seen, lua_topointer(L, index)))
+                       return NULL;
+
                max = _lua_test_array(L, index);
 
                if (max >= 0)
@@ -262,12 +310,15 @@ static struct json_object * _lua_to_json(lua_State *L, int index)
                        if (!obj)
                                return NULL;
 
+                       if (!lua_checkstack(L, 1))
+                               return NULL;
+
                        for (i = 1; i <= max; i++)
                        {
                                lua_rawgeti(L, index, i);
 
                                json_object_array_put_idx(obj, i - 1,
-                                                         _lua_to_json(L, lua_gettop(L)));
+                                                         _lua_to_json_rec(L, -1, seen));
 
                                lua_pop(L, 1);
                        }
@@ -280,6 +331,9 @@ static struct json_object * _lua_to_json(lua_State *L, int index)
                if (!obj)
                        return NULL;
 
+               if (!lua_checkstack(L, 3))
+                       return NULL;
+
                lua_pushnil(L);
 
                while (lua_next(L, index))
@@ -289,7 +343,7 @@ static struct json_object * _lua_to_json(lua_State *L, int index)
 
                        if (key)
                                json_object_object_add(obj, key,
-                                                      _lua_to_json(L, lua_gettop(L) - 1));
+                                                      _lua_to_json_rec(L, -2, seen));
 
                        lua_pop(L, 2);
                }
@@ -318,6 +372,23 @@ static struct json_object * _lua_to_json(lua_State *L, int index)
        return NULL;
 }
 
+static struct json_object * _lua_to_json(lua_State *L, int index)
+{
+       struct seen *s = calloc(sizeof(struct seen) + sizeof(void *) * 10, 1);
+       struct json_object *rv;
+
+       if (!s)
+               return NULL;
+
+       s->size = 10;
+
+       rv = _lua_to_json_rec(L, index, &s);
+
+       free(s);
+
+       return rv;
+}
+
 static int json_parse_set(lua_State *L)
 {
        struct json_state *s = luaL_checkudata(L, 1, LUCI_JSONC_PARSER);