diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2022-05-11 02:12:57 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-11 02:12:57 +0900 |
commit | 97cda6e10f68b0fd227e0dac35d3e8614ae76d80 (patch) | |
tree | cec97c845a109e5e8e6f49d268b8587615bf00d2 | |
parent | 82544241bade82d0740378fa099d0d6c3dcfaeb0 (diff) | |
parent | 99b1145aae682ddd7554c7e3ac5ebf778e88f87d (diff) | |
download | systemd-97cda6e10f68b0fd227e0dac35d3e8614ae76d80.tar.gz |
Merge pull request #23335 from keszybz/fuzz-json-more-coverage
More coverage in fuzz-json
-rw-r--r-- | src/basic/alloc-util.h | 4 | ||||
-rw-r--r-- | src/fuzz/fuzz-json.c | 99 | ||||
-rw-r--r-- | src/shared/json.c | 231 | ||||
-rw-r--r-- | src/shared/json.h | 8 | ||||
-rw-r--r-- | test/fuzz/fuzz-json/leak-normalize-fail | 1 | ||||
-rw-r--r-- | test/fuzz/fuzz-json/leak-normalize-object | 1 | ||||
-rw-r--r-- | test/fuzz/fuzz-json/leak-sort | 1 |
7 files changed, 201 insertions, 144 deletions
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 9dde770dab..155fbf5f1f 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -55,8 +55,8 @@ typedef void* (*mfree_func_t)(void *p); typeof(a)* _a = &(a); \ typeof(b)* _b = &(b); \ free(*_a); \ - (*_a) = (*_b); \ - (*_b) = NULL; \ + *_a = *_b; \ + *_b = NULL; \ 0; \ }) diff --git a/src/fuzz/fuzz-json.c b/src/fuzz/fuzz-json.c index ad7460c6fd..c393fcf394 100644 --- a/src/fuzz/fuzz-json.c +++ b/src/fuzz/fuzz-json.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "env-util.h" #include "fd-util.h" #include "fuzz.h" #include "json.h" @@ -10,18 +11,106 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { size_t out_size; _cleanup_fclose_ FILE *f = NULL, *g = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + int r; + + /* Disable most logging if not running standalone */ + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); f = data_to_file(data, size); assert_se(f); - if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0) + r = json_parse_file(f, NULL, 0, &v, NULL, NULL); + if (r < 0) { + log_debug_errno(r, "failed to parse input: %m"); return 0; + } + + if (getenv_bool("SYSTEMD_FUZZ_OUTPUT") <= 0) + assert_se(g = open_memstream_unlocked(&out, &out_size)); + + json_variant_dump(v, 0, g ?: stdout, NULL); + json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g ?: stdout, NULL); + + bool sorted = json_variant_is_sorted(v); + log_debug("json_variant_is_sorted: %s", yes_no(sorted)); + + r = json_variant_sort(&v); + log_debug_errno(r, "json_variant_sort: %d/%m", r); + + sorted = json_variant_is_sorted(v); + log_debug("json_variant_is_sorted: %s", yes_no(sorted)); + assert_se(r < 0 || sorted); + + bool normalized = json_variant_is_normalized(v); + log_debug("json_variant_is_normalized: %s", yes_no(normalized)); + + r = json_variant_normalize(&v); + log_debug_errno(r, "json_variant_normalize: %d/%m", r); + + normalized = json_variant_is_normalized(v); + log_debug("json_variant_is_normalized: %s", yes_no(normalized)); + assert_se(r < 0 || normalized); + + double real = json_variant_real(v); + log_debug("json_variant_real: %lf", real); + + bool negative = json_variant_is_negative(v); + log_debug("json_variant_is_negative: %s", yes_no(negative)); + + bool blank = json_variant_is_blank_object(v); + log_debug("json_variant_is_blank_object: %s", yes_no(blank)); + + blank = json_variant_is_blank_array(v); + log_debug("json_variant_is_blank_array: %s", yes_no(blank)); + + size_t elements = json_variant_elements(v); + log_debug("json_variant_elements: %zu", elements); + + for (size_t i = 0; i <= elements + 2; i++) + (void) json_variant_by_index(v, i); + + assert_se(json_variant_equal(v, v)); + assert_se(!json_variant_equal(v, NULL)); + assert_se(!json_variant_equal(NULL, v)); + + bool sensitive = json_variant_is_sensitive(v); + log_debug("json_variant_is_sensitive: %s", yes_no(sensitive)); + + json_variant_sensitive(v); + + sensitive = json_variant_is_sensitive(v); + log_debug("json_variant_is_sensitive: %s", yes_no(sensitive)); + + const char *source; + unsigned line, column; + assert_se(json_variant_get_source(v, &source, &line, &column) == 0); + log_debug("json_variant_get_source: %s:%u:%u", source ?: "-", line, column); + + r = json_variant_set_field_string(&v, "a", "string-a"); + log_debug_errno(r, "json_set_field_string: %d/%m", r); + + r = json_variant_set_field_integer(&v, "b", -12345); + log_debug_errno(r, "json_set_field_integer: %d/%m", r); + + r = json_variant_set_field_unsigned(&v, "c", 12345); + log_debug_errno(r, "json_set_field_unsigned: %d/%m", r); + + r = json_variant_set_field_boolean(&v, "d", false); + log_debug_errno(r, "json_set_field_boolean: %d/%m", r); + + r = json_variant_set_field_strv(&v, "e", STRV_MAKE("e-1", "e-2", "e-3")); + log_debug_errno(r, "json_set_field_strv: %d/%m", r); + + r = json_variant_filter(&v, STRV_MAKE("a", "b", "c", "d", "e")); + log_debug_errno(r, "json_variant_filter: %d/%m", r); - g = open_memstream_unlocked(&out, &out_size); - assert_se(g); + /* I assume we can merge v with itself… */ + r = json_variant_merge(&v, v); + log_debug_errno(r, "json_variant_merge: %d/%m", r); - json_variant_dump(v, 0, g, NULL); - json_variant_dump(v, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR|JSON_FORMAT_SOURCE, g, NULL); + r = json_variant_append_array(&v, v); + log_debug_errno(r, "json_variant_append_array: %d/%m", r); return 0; } diff --git a/src/shared/json.c b/src/shared/json.c index f66b7df24c..bcc109abc2 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -273,8 +273,8 @@ static JsonVariant *json_variant_formalize(JsonVariant *v) { static JsonVariant *json_variant_conservative_formalize(JsonVariant *v) { - /* Much like json_variant_formalize(), but won't simplify if the variant has a source/line location attached to - * it, in order not to lose context */ + /* Much like json_variant_formalize(), but won't simplify if the variant has a source/line location + * attached to it, in order not to lose context */ if (!v) return NULL; @@ -546,7 +546,7 @@ int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) { for (v->n_elements = 0; v->n_elements < n; v->n_elements++) { JsonVariant *w = v + 1 + v->n_elements, - *c = array[v->n_elements]; + *c = array[v->n_elements]; uint16_t d; d = json_variant_depth(c); @@ -574,9 +574,6 @@ int json_variant_new_array(JsonVariant **ret, JsonVariant **array, size_t n) { } int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) { - JsonVariant *v; - size_t i; - assert_return(ret, -EINVAL); if (n == 0) { *ret = JSON_VARIANT_MAGIC_EMPTY_ARRAY; @@ -584,7 +581,7 @@ int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) { } assert_return(p, -EINVAL); - v = new(JsonVariant, n + 1); + JsonVariant *v = new(JsonVariant, n + 1); if (!v) return -ENOMEM; @@ -595,7 +592,7 @@ int json_variant_new_array_bytes(JsonVariant **ret, const void *p, size_t n) { .depth = 1, }; - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { JsonVariant *w = v + 1 + i; *w = (JsonVariant) { @@ -693,7 +690,7 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) { for (v->n_elements = 0; v->n_elements < n; v->n_elements++) { JsonVariant *w = v + 1 + v->n_elements, - *c = array[v->n_elements]; + *c = array[v->n_elements]; uint16_t d; if ((v->n_elements & 1) == 0) { @@ -734,7 +731,6 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) { } static size_t json_variant_size(JsonVariant* v) { - if (!json_variant_is_regular(v)) return 0; @@ -790,12 +786,9 @@ static void json_variant_free_inner(JsonVariant *v, bool force_sensitive) { return; } - if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) { - size_t i; - - for (i = 0; i < v->n_elements; i++) + if (IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) + for (size_t i = 0; i < v->n_elements; i++) json_variant_free_inner(v + 1 + i, sensitive); - } if (sensitive) explicit_bzero_safe(v, json_variant_size(v)); @@ -839,11 +832,9 @@ JsonVariant *json_variant_unref(JsonVariant *v) { } void json_variant_unref_many(JsonVariant **array, size_t n) { - size_t i; - assert(array || n == 0); - for (i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) json_variant_unref(array[i]); } @@ -1218,8 +1209,6 @@ mismatch: } JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVariant **ret_key) { - size_t i; - if (!v) goto not_found; if (!key) @@ -1241,6 +1230,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria while (b > a) { JsonVariant *p; const char *f; + size_t i; int c; i = (a + b) / 2; @@ -1264,7 +1254,7 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria } /* The variant is not sorted, hence search for the field linearly */ - for (i = 0; i < v->n_elements; i += 2) { + for (size_t i = 0; i < v->n_elements; i += 2) { JsonVariant *p; p = json_variant_dereference(v + 1 + i); @@ -1335,34 +1325,28 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) { return true; case JSON_VARIANT_ARRAY: { - size_t i, n; - - n = json_variant_elements(a); + size_t n = json_variant_elements(a); if (n != json_variant_elements(b)) return false; - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) if (!json_variant_equal(json_variant_by_index(a, i), json_variant_by_index(b, i))) return false; - } return true; } case JSON_VARIANT_OBJECT: { - size_t i, n; - - n = json_variant_elements(a); + size_t n = json_variant_elements(a); if (n != json_variant_elements(b)) return false; /* Iterate through all keys in 'a' */ - for (i = 0; i < n; i += 2) { + for (size_t i = 0; i < n; i += 2) { bool found = false; - size_t j; /* Match them against all keys in 'b' */ - for (j = 0; j < n; j += 2) { + for (size_t j = 0; j < n; j += 2) { JsonVariant *key_b; key_b = json_variant_by_index(b, j); @@ -1470,16 +1454,14 @@ static int print_source(FILE *f, JsonVariant *v, JsonFormatFlags flags, bool whi DECIMAL_STR_MAX(unsigned) -1; if (whitespace) { - size_t i, n; - - n = 1 + (v->source ? strlen(v->source->name) : 0) + - ((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) + - (v->line > 0 ? w : 0) + - (((v->source || v->line > 0) && v->column > 0) ? 1 : 0) + - (v->column > 0 ? k : 0) + - 2; - - for (i = 0; i < n; i++) + size_t n = 1 + (v->source ? strlen(v->source->name) : 0) + + ((v->source && (v->line > 0 || v->column > 0)) ? 1 : 0) + + (v->line > 0 ? w : 0) + + (((v->source || v->line > 0) && v->column > 0) ? 1 : 0) + + (v->column > 0 ? k : 0) + + 2; + + for (size_t i = 0; i < n; i++) fputc(' ', f); } else { fputc('[', f); @@ -1631,10 +1613,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha break; case JSON_VARIANT_ARRAY: { - size_t i, n; - - n = json_variant_elements(v); - + size_t n = json_variant_elements(v); if (n == 0) fputs("[]", f); else { @@ -1653,7 +1632,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha fputc('[', f); } - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { JsonVariant *e; assert_se(e = json_variant_by_index(v, i)); @@ -1687,10 +1666,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha } case JSON_VARIANT_OBJECT: { - size_t i, n; - - n = json_variant_elements(v); - + size_t n = json_variant_elements(v); if (n == 0) fputs("{}", f); else { @@ -1709,7 +1685,7 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha fputc('{', f); } - for (i = 0; i < n; i += 2) { + for (size_t i = 0; i < n; i += 2) { JsonVariant *e; e = json_variant_by_index(v, i); @@ -1826,7 +1802,7 @@ void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const cha int json_variant_filter(JsonVariant **v, char **to_remove) { _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; _cleanup_free_ JsonVariant **array = NULL; - size_t i, n = 0, k = 0; + size_t n = 0, k = 0; int r; assert(v); @@ -1839,7 +1815,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) { if (strv_isempty(to_remove)) return 0; - for (i = 0; i < json_variant_elements(*v); i += 2) { + for (size_t i = 0; i < json_variant_elements(*v); i += 2) { JsonVariant *p; p = json_variant_by_index(*v, i); @@ -1871,9 +1847,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) { return r; json_variant_propagate_sensitive(*v, w); - - json_variant_unref(*v); - *v = TAKE_PTR(w); + JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); return (int) n; } @@ -1881,7 +1855,7 @@ int json_variant_filter(JsonVariant **v, char **to_remove) { int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value) { _cleanup_(json_variant_unrefp) JsonVariant *field_variant = NULL, *w = NULL; _cleanup_free_ JsonVariant **array = NULL; - size_t i, k = 0; + size_t k = 0; int r; assert(v); @@ -1896,7 +1870,7 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu if (!json_variant_is_object(*v)) return -EINVAL; - for (i = 0; i < json_variant_elements(*v); i += 2) { + for (size_t i = 0; i < json_variant_elements(*v); i += 2) { JsonVariant *p; p = json_variant_by_index(*v, i); @@ -1942,9 +1916,7 @@ int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *valu return r; json_variant_propagate_sensitive(*v, w); - - json_variant_unref(*v); - *v = TAKE_PTR(w); + JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); return 1; } @@ -2007,7 +1979,7 @@ int json_variant_set_field_strv(JsonVariant **v, const char *field, char **l) { int json_variant_merge(JsonVariant **v, JsonVariant *m) { _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; _cleanup_free_ JsonVariant **array = NULL; - size_t v_elements, m_elements, i, k; + size_t v_elements, m_elements, k; bool v_blank, m_blank; int r; @@ -2025,8 +1997,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) { return 0; /* nothing to do */ if (v_blank) { - json_variant_unref(*v); - *v = json_variant_ref(m); + JSON_VARIANT_REPLACE(*v, json_variant_ref(m)); return 1; } @@ -2040,7 +2011,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) { return -ENOMEM; k = 0; - for (i = 0; i < v_elements; i += 2) { + for (size_t i = 0; i < v_elements; i += 2) { JsonVariant *u; u = json_variant_by_index(*v, i); @@ -2054,7 +2025,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) { array[k++] = json_variant_by_index(*v, i + 1); } - for (i = 0; i < m_elements; i++) + for (size_t i = 0; i < m_elements; i++) array[k++] = json_variant_by_index(m, i); r = json_variant_new_object(&w, array, k); @@ -2063,9 +2034,7 @@ int json_variant_merge(JsonVariant **v, JsonVariant *m) { json_variant_propagate_sensitive(*v, w); json_variant_propagate_sensitive(m, w); - - json_variant_unref(*v); - *v = TAKE_PTR(w); + JSON_VARIANT_REPLACE(*v, TAKE_PTR(w)); return 1; } @@ -2089,34 +2058,29 @@ int json_variant_append_array(JsonVariant **v, JsonVariant *element) { if (blank) r = json_variant_new_array(&nv, (JsonVariant*[]) { element }, 1); else { - _cleanup_free_ JsonVariant **array = NULL; - size_t i; - - array = new(JsonVariant*, json_variant_elements(*v) + 1); + _cleanup_free_ JsonVariant **array = new(JsonVariant*, json_variant_elements(*v) + 1); if (!array) return -ENOMEM; - for (i = 0; i < json_variant_elements(*v); i++) + size_t size = json_variant_elements(*v); + for (size_t i = 0; i < size; i++) array[i] = json_variant_by_index(*v, i); - array[i] = element; + array[size] = element; - r = json_variant_new_array(&nv, array, i + 1); + r = json_variant_new_array(&nv, array, size + 1); } if (r < 0) return r; json_variant_propagate_sensitive(*v, nv); - - json_variant_unref(*v); - *v = TAKE_PTR(nv); + JSON_VARIANT_REPLACE(*v, TAKE_PTR(nv)); return 0; } int json_variant_strv(JsonVariant *v, char ***ret) { char **l = NULL; - size_t n, i; bool sensitive; int r; @@ -2136,12 +2100,12 @@ int json_variant_strv(JsonVariant *v, char ***ret) { sensitive = v->sensitive; - n = json_variant_elements(v); + size_t n = json_variant_elements(v); l = new(char*, n+1); if (!l) return -ENOMEM; - for (i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { JsonVariant *e; assert_se(e = json_variant_by_index(v, i)); @@ -2160,7 +2124,7 @@ int json_variant_strv(JsonVariant *v, char ***ret) { } } - l[i] = NULL; + l[n] = NULL; *ret = TAKE_PTR(l); return 0; @@ -2279,8 +2243,9 @@ static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned assert(v); - /* Patch in source and line/column number. Tries to do this in-place if the caller is the sole referencer of - * the object. If not, allocates a new object, possibly a surrogate for the original one */ + /* Patch in source and line/column number. Tries to do this in-place if the caller is the sole + * referencer of the object. If not, allocates a new object, possibly a surrogate for the original + * one */ if (!*v) return 0; @@ -2323,8 +2288,7 @@ static int json_variant_set_source(JsonVariant **v, JsonSource *source, unsigned w->line = line; w->column = column; - json_variant_unref(*v); - *v = w; + JSON_VARIANT_REPLACE(*v, w); return 1; } @@ -2847,7 +2811,7 @@ static int json_parse_internal( unsigned *column, bool continue_end) { - size_t n_stack = 1, i; + size_t n_stack = 1; unsigned line_buffer = 0, column_buffer = 0; void *tokenizer_state = NULL; JsonStack *stack = NULL; @@ -3186,7 +3150,7 @@ done: r = 0; finish: - for (i = 0; i < n_stack; i++) + for (size_t i = 0; i < n_stack; i++) json_stack_release(stack + i); free(stack); @@ -3229,7 +3193,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla int json_buildv(JsonVariant **ret, va_list ap) { JsonStack *stack = NULL; - size_t n_stack = 1, i; + size_t n_stack = 1; int r; assert_return(ret, -EINVAL); @@ -3757,10 +3721,10 @@ int json_buildv(JsonVariant **ret, va_list ap) { stack[n_stack++] = (JsonStack) { .expect = EXPECT_OBJECT_KEY, - .n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* if we shall suppress the - * new object, then we should - * also suppress all object - * members */ + .n_suppress = current->n_suppress != 0 ? SIZE_MAX : 0, /* If we shall suppress the + * new object, then we should + * also suppress all object + * members. */ }; break; @@ -4128,9 +4092,9 @@ int json_buildv(JsonVariant **ret, va_list ap) { current->elements[current->n_elements++] = TAKE_PTR(add_more); } - /* If we are supposed to suppress items, let's subtract how many items where generated from that - * counter. Except if the counter is SIZE_MAX, i.e. we shall suppress an infinite number of elements - * on this stack level */ + /* If we are supposed to suppress items, let's subtract how many items where generated from + * that counter. Except if the counter is SIZE_MAX, i.e. we shall suppress an infinite number + * of elements on this stack level */ if (current->n_suppress != SIZE_MAX) { if (current->n_suppress <= n_subtract) /* Saturated */ current->n_suppress = 0; @@ -4147,7 +4111,7 @@ done: r = 0; finish: - for (i = 0; i < n_stack; i++) + for (size_t i = 0; i < n_stack; i++) json_stack_release(stack + i); free(stack); @@ -4231,8 +4195,7 @@ int json_log_internal( } int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallback bad, JsonDispatchFlags flags, void *userdata) { - const JsonDispatch *p; - size_t i, n, m; + size_t m; int r, done = 0; bool *found; @@ -4245,14 +4208,16 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba return -EINVAL; } - for (p = table, m = 0; p->name; p++) + m = 0; + for (const JsonDispatch *p = table; p->name; p++) m++; found = newa0(bool, m); - n = json_variant_elements(v); - for (i = 0; i < n; i += 2) { + size_t n = json_variant_elements(v); + for (size_t i = 0; i < n; i += 2) { JsonVariant *key, *value; + const JsonDispatch *p; assert_se(key = json_variant_by_index(v, i)); assert_se(value = json_variant_by_index(v, i+1)); @@ -4326,7 +4291,7 @@ int json_dispatch(JsonVariant *v, const JsonDispatch table[], JsonDispatchCallba } } - for (p = table; p->name; p++) { + for (const JsonDispatch *p = table; p->name; p++) { JsonDispatchFlags merged_flags = p->flags | flags; if ((merged_flags & JSON_MANDATORY) && !found[p-table]) { @@ -4524,14 +4489,10 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags } int json_dispatch_variant(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { - JsonVariant **p = userdata; - + JsonVariant **p = ASSERT_PTR(userdata); assert(variant); - assert(p); - - json_variant_unref(*p); - *p = json_variant_ref(variant); + JSON_VARIANT_REPLACE(*p, json_variant_ref(variant)); return 0; } @@ -4620,8 +4581,8 @@ static int json_cmp_strings(const void *x, const void *y) { int json_variant_sort(JsonVariant **v) { _cleanup_free_ JsonVariant **a = NULL; - JsonVariant *n = NULL; - size_t i, m; + _cleanup_(json_variant_unrefp) JsonVariant *n = NULL; + size_t m; int r; assert(v); @@ -4639,7 +4600,7 @@ int json_variant_sort(JsonVariant **v) { if (!a) return -ENOMEM; - for (i = 0; i < m; i++) + for (size_t i = 0; i < m; i++) a[i] = json_variant_by_index(*v, i); qsort(a, m/2, sizeof(JsonVariant*)*2, json_cmp_strings); @@ -4653,16 +4614,15 @@ int json_variant_sort(JsonVariant **v) { if (!n->sorted) /* Check if this worked. This will fail if there are multiple identical keys used. */ return -ENOTUNIQ; - json_variant_unref(*v); - *v = n; + JSON_VARIANT_REPLACE(*v, TAKE_PTR(n)); return 1; } int json_variant_normalize(JsonVariant **v) { _cleanup_free_ JsonVariant **a = NULL; - JsonVariant *n = NULL; - size_t i, j, m; + _cleanup_(json_variant_unrefp) JsonVariant *n = NULL; + size_t i, m; int r; assert(v); @@ -4680,10 +4640,11 @@ int json_variant_normalize(JsonVariant **v) { if (!a) return -ENOMEM; - for (i = 0; i < m; i++) { + for (i = 0; i < m; ) { a[i] = json_variant_ref(json_variant_by_index(*v, i)); + i++; - r = json_variant_normalize(a + i); + r = json_variant_normalize(&a[i-1]); if (r < 0) goto finish; } @@ -4708,23 +4669,21 @@ int json_variant_normalize(JsonVariant **v) { goto finish; } - json_variant_unref(*v); - *v = n; + JSON_VARIANT_REPLACE(*v, TAKE_PTR(n)); r = 1; finish: - for (j = 0; j < i; j++) + for (size_t j = 0; j < i; j++) json_variant_unref(a[j]); return r; } bool json_variant_is_normalized(JsonVariant *v) { - - /* For now, let's consider anything containing numbers not expressible as integers as - * non-normalized. That's because we cannot sensibly compare them due to accuracy issues, nor even - * store them if they are too large. */ + /* For now, let's consider anything containing numbers not expressible as integers as non-normalized. + * That's because we cannot sensibly compare them due to accuracy issues, nor even store them if they + * are too large. */ if (json_variant_is_real(v) && !json_variant_is_integer(v) && !json_variant_is_unsigned(v)) return false; @@ -4754,7 +4713,6 @@ bool json_variant_is_sorted(JsonVariant *v) { } int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) { - if (!json_variant_is_string(v)) return -EINVAL; @@ -4762,7 +4720,6 @@ int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) { } int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) { - if (!json_variant_is_string(v)) return -EINVAL; @@ -4770,15 +4727,15 @@ int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) { } static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = { - [JSON_VARIANT_STRING] = "string", - [JSON_VARIANT_INTEGER] = "integer", + [JSON_VARIANT_STRING] = "string", + [JSON_VARIANT_INTEGER] = "integer", [JSON_VARIANT_UNSIGNED] = "unsigned", - [JSON_VARIANT_REAL] = "real", - [JSON_VARIANT_NUMBER] = "number", - [JSON_VARIANT_BOOLEAN] = "boolean", - [JSON_VARIANT_ARRAY] = "array", - [JSON_VARIANT_OBJECT] = "object", - [JSON_VARIANT_NULL] = "null", + [JSON_VARIANT_REAL] = "real", + [JSON_VARIANT_NUMBER] = "number", + [JSON_VARIANT_BOOLEAN] = "boolean", + [JSON_VARIANT_ARRAY] = "array", + [JSON_VARIANT_OBJECT] = "object", + [JSON_VARIANT_NULL] = "null", }; DEFINE_STRING_TABLE_LOOKUP(json_variant_type, JsonVariantType); diff --git a/src/shared/json.h b/src/shared/json.h index 91d02a911e..98d184c309 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -82,6 +82,14 @@ JsonVariant *json_variant_ref(JsonVariant *v); JsonVariant *json_variant_unref(JsonVariant *v); void json_variant_unref_many(JsonVariant **array, size_t n); +#define JSON_VARIANT_REPLACE(v, q) \ + do { \ + typeof(v)* _v = &(v); \ + typeof(q) _q = (q); \ + json_variant_unref(*_v); \ + *_v = _q; \ + } while(0) + DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref); const char *json_variant_string(JsonVariant *v); diff --git a/test/fuzz/fuzz-json/leak-normalize-fail b/test/fuzz/fuzz-json/leak-normalize-fail new file mode 100644 index 0000000000..b247ccd199 --- /dev/null +++ b/test/fuzz/fuzz-json/leak-normalize-fail @@ -0,0 +1 @@ +[7E73]
\ No newline at end of file diff --git a/test/fuzz/fuzz-json/leak-normalize-object b/test/fuzz/fuzz-json/leak-normalize-object new file mode 100644 index 0000000000..0a8caa426c --- /dev/null +++ b/test/fuzz/fuzz-json/leak-normalize-object @@ -0,0 +1 @@ +[7,7,7,7,{"":7,"":7,"^t":7,"-":7},2777,7,7,7,3]
\ No newline at end of file diff --git a/test/fuzz/fuzz-json/leak-sort b/test/fuzz/fuzz-json/leak-sort new file mode 100644 index 0000000000..f8446dbdc7 --- /dev/null +++ b/test/fuzz/fuzz-json/leak-sort @@ -0,0 +1 @@ +{"":2,"":6,"-":7}
\ No newline at end of file |