]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-json: limit the stack depth during parsing as well
authorFrantisek Sumsal <frantisek@sumsal.cz>
Fri, 10 Apr 2026 15:20:03 +0000 (17:20 +0200)
committerLuca Boccassi <luca.boccassi@gmail.com>
Fri, 10 Apr 2026 20:33:11 +0000 (21:33 +0100)
src/libsystemd/sd-json/sd-json.c
src/test/test-json.c

index 6245d471b7a6fb13472e8c3d1f617950e2cdc6c2..1fd006c7d9572e587f17c274c933e0f191558bec 100644 (file)
@@ -3122,6 +3122,12 @@ static int json_parse_internal(
                                 goto finish;
                         }
 
+                        /* n_stack includes the top level entry, hence > instead of >= */
+                        if (n_stack > DEPTH_MAX) {
+                                r = -ELNRNG;
+                                goto finish;
+                        }
+
                         if (!GREEDY_REALLOC(stack, n_stack+1)) {
                                 r = -ENOMEM;
                                 goto finish;
@@ -3178,6 +3184,12 @@ static int json_parse_internal(
                                 goto finish;
                         }
 
+                        /* n_stack includes the top level entry, hence > instead of >= */
+                        if (n_stack > DEPTH_MAX) {
+                                r = -ELNRNG;
+                                goto finish;
+                        }
+
                         if (!GREEDY_REALLOC(stack, n_stack+1)) {
                                 r = -ENOMEM;
                                 goto finish;
index 016416dc4b7288624f00a22bbbd2f26b1cd75c5a..8e2c8621c1f6da26e95a6b6553f0fb6b6ca14ad7 100644 (file)
@@ -556,6 +556,49 @@ TEST(depth) {
         fputs("\n", stdout);
 }
 
+static char *prepare_nested_json(const char *open, unsigned depth) {
+        char *s, *p;
+        size_t olen;
+
+        assert_se(open);
+
+        olen = strlen(open);
+        s = p = new(char, olen * depth + 1);
+        if (!s)
+                return NULL;
+
+        for (unsigned i = 0; i < depth; i++)
+                p = mempcpy(p, open, olen);
+        *p = '\0';
+
+        return s;
+}
+
+TEST(parse_depth) {
+        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
+        _cleanup_free_ char *s = NULL;
+
+        /* Refuse parsing > DEPTH_MAX (currently 2048) levels of nested arrays */
+        s = prepare_nested_json("[", 2049);
+        ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG);
+        s = mfree(s);
+
+        /* Same for nested objects */
+        s = prepare_nested_json("{\"a\":", 2049);
+        ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), ELNRNG);
+        s = mfree(s);
+
+        /* <= DEPTH_MAX levels of nested arrays should be refused by EINVAL
+         * later in the parsing process */
+        s = prepare_nested_json("[", 2048);
+        ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL);
+        s = mfree(s);
+
+        /* And the same for nested objects */
+        s = prepare_nested_json("{\"a\":", 2048);
+        ASSERT_ERROR(sd_json_parse(s, 0, &v, NULL, NULL), EINVAL);
+}
+
 TEST(normalize) {
         _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL, *w = NULL;
         _cleanup_free_ char *t = NULL;