#include "terminal-util.h"
#include "utf8.h"
+/* Refuse putting together variants with a larger depth than 16K by default (as a protection against overflowing stacks
+ * if code processes JSON objects recursively. Note that we store the depth in an uint16_t, hence make sure this
+ * remains under 2^16. */
+#define DEPTH_MAX (16U*1024U)
+assert_cc(DEPTH_MAX <= UINT16_MAX);
+
typedef struct JsonSource {
/* When we parse from a file or similar, encodes the filename, to indicate the source of a json variant */
size_t n_ref;
/* While comparing two arrays, we use this for marking what we already have seen */
bool is_marked:1;
+ /* The current 'depth' of the JsonVariant, i.e. how many levels of member variants this has */
+ uint16_t depth;
+
union {
/* For simple types we store the value in-line. */
JsonValue value;
return json_variant_dereference(v->reference);
}
+static uint16_t json_variant_depth(JsonVariant *v) {
+
+ v = json_variant_dereference(v);
+ if (!v)
+ return 0;
+
+ if (json_variant_is_magic(v))
+ return 0;
+
+ return v->depth;
+}
+
static JsonVariant *json_variant_normalize(JsonVariant *v) {
/* Converts json variants to their normalized form, i.e. fully dereferenced and wherever possible converted to
}
int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) {
- JsonVariant *v;
- size_t i;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
assert_return(ret, -EINVAL);
if (n == 0) {
*v = (JsonVariant) {
.n_ref = 1,
.type = JSON_VARIANT_ARRAY,
- .n_elements = n,
};
- for (i = 0; i < n; i++) {
- JsonVariant *w = v + 1 + i;
+ for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
+ JsonVariant *w = v + 1 + v->n_elements,
+ *c = array[v->n_elements];
+ uint16_t d;
+
+ d = json_variant_depth(c);
+ if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+ return -ELNRNG;
+ if (d >= v->depth)
+ v->depth = d + 1;
*w = (JsonVariant) {
.is_embedded = true,
.parent = v,
};
- json_variant_set(w, array[i]);
- json_variant_copy_source(w, array[i]);
+ json_variant_set(w, c);
+ json_variant_copy_source(w, c);
}
- *ret = v;
+ *ret = TAKE_PTR(v);
return 0;
}
.n_ref = 1,
.type = JSON_VARIANT_ARRAY,
.n_elements = n,
+ .depth = 1,
};
for (i = 0; i < n; i++) {
*v = (JsonVariant) {
.n_ref = 1,
.type = JSON_VARIANT_ARRAY,
+ .depth = 1,
};
for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
}
int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) {
- JsonVariant *v;
- size_t i;
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
assert_return(ret, -EINVAL);
if (n == 0) {
*v = (JsonVariant) {
.n_ref = 1,
.type = JSON_VARIANT_OBJECT,
- .n_elements = n,
};
- for (i = 0; i < n; i++) {
- JsonVariant *w = v + 1 + i;
+ for (v->n_elements = 0; v->n_elements < n; v->n_elements++) {
+ JsonVariant *w = v + 1 + v->n_elements,
+ *c = array[v->n_elements];
+ uint16_t d;
+
+ d = json_variant_depth(c);
+ if (d >= DEPTH_MAX) /* Refuse too deep nesting */
+ return -ELNRNG;
+ if (d >= v->depth)
+ v->depth = d + 1;
*w = (JsonVariant) {
.is_embedded = true,
.parent = v,
};
- json_variant_set(w, array[i]);
- json_variant_copy_source(w, array[i]);
+ json_variant_set(w, c);
+ json_variant_copy_source(w, c);
}
- *ret = v;
+ *ret = TAKE_PTR(v);
return 0;
}
printf("--- pretty end ---\n");
}
+static void test_depth(void) {
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *k = NULL;
+ unsigned i;
+ int r;
+
+ assert_se(json_variant_new_string(&k, "hallo") >= 0);
+ v = json_variant_ref(k);
+
+ /* Let's verify that the maximum depth checks work */
+
+ for (i = 0;; i++) {
+ _cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
+
+ assert_se(i <= UINT16_MAX);
+ if (i & 1)
+ r = json_variant_new_array(&w, &v, 1);
+ else
+ r = json_variant_new_object(&w, (JsonVariant*[]) { k, v }, 2);
+ if (r == -ELNRNG) {
+ log_info("max depth at %u", i);
+ break;
+ }
+
+ assert_se(r >= 0);
+
+ json_variant_unref(v);
+ v = TAKE_PTR(w);
+ }
+
+ json_variant_dump(v, 0, stdout, NULL);
+}
+
int main(int argc, char *argv[]) {
log_set_max_level(LOG_DEBUG);
test_source();
+ test_depth();
+
return 0;
}