Coder Social home page Coder Social logo

json.h's Introduction

๐Ÿ—„๏ธ json.h

Actions Status Build status Sponsor

A simple single header solution to parsing JSON in C and C++.

JSON is parsed into a read-only, single allocation buffer.

The current supported compilers are gcc, clang and msvc.

The current supported platforms are Windows, mac OS and Linux.

Usage

Just #include "json.h" in your code!

json_parse

Parse a json string into a DOM.

struct json_value_s *json_parse(
    const void *src,
    size_t src_size);
  • src - a utf-8 json string to parse.
  • src_size - the size of src in bytes.

Returns a struct json_value_s* pointing the root of the json DOM.

struct json_value_s

The main struct for interacting with a parsed JSON Document Object Model (DOM) is the struct json_value_s.

struct json_value_s {
  void *payload;
  size_t type;
};
  • payload - a pointer to the contents of the value.
  • type - the type of struct payload points to, one of json_type_e. Note: if type is json_type_true, json_type_false, or json_type_null, payload will be NULL.

json_parse_ex

Extended parse a json string into a DOM.

struct json_value_s *json_parse_ex(
    const void *src,
    size_t src_size,
    size_t flags_bitset,
    void*(*alloc_func_ptr)(void *, size_t),
    void *user_data,
    struct json_parse_result_s *result);
  • src - a utf-8 json string to parse.
  • src_size - the size of src in bytes.
  • flags_bitset - extra parsing flags, a bitset of flags specified in enum json_parse_flags_e.
  • alloc_func_ptr - a callback function to use for doing the single allocation. If NULL, malloc() is used.
  • user_data - user data to be passed as the first argument to alloc_func_ptr.
  • result - the result of the parsing. If a parsing error occurred this will contain what type of error, and where in the source it occurred. Can be NULL.

Returns a struct json_value_s* pointing the root of the json DOM.

enum json_parse_flags_e

The extra parsing flags that can be specified to json_parse_ex() are as follows:

enum json_parse_flags_e {
  json_parse_flags_default = 0,
  json_parse_flags_allow_trailing_comma = 0x1,
  json_parse_flags_allow_unquoted_keys = 0x2,
  json_parse_flags_allow_global_object = 0x4,
  json_parse_flags_allow_equals_in_object = 0x8,
  json_parse_flags_allow_no_commas = 0x10,
  json_parse_flags_allow_c_style_comments = 0x20,
  json_parse_flags_deprecated = 0x40,
  json_parse_flags_allow_location_information = 0x80,
  json_parse_flags_allow_single_quoted_strings = 0x100,
  json_parse_flags_allow_hexadecimal_numbers = 0x200,
  json_parse_flags_allow_leading_plus_sign = 0x400,
  json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800,
  json_parse_flags_allow_inf_and_nan = 0x1000,
  json_parse_flags_allow_multi_line_strings = 0x2000,
  json_parse_flags_allow_simplified_json =
      (json_parse_flags_allow_trailing_comma |
       json_parse_flags_allow_unquoted_keys |
       json_parse_flags_allow_global_object |
       json_parse_flags_allow_equals_in_object |
       json_parse_flags_allow_no_commas),
  json_parse_flags_allow_json5 =
      (json_parse_flags_allow_trailing_comma |
       json_parse_flags_allow_unquoted_keys |
       json_parse_flags_allow_c_style_comments |
       json_parse_flags_allow_single_quoted_strings |
       json_parse_flags_allow_hexadecimal_numbers |
       json_parse_flags_allow_leading_plus_sign |
       json_parse_flags_allow_leading_or_trailing_decimal_point |
       json_parse_flags_allow_inf_and_nan |
       json_parse_flags_allow_multi_line_strings)
};
  • json_parse_flags_default - the default, no special behaviour is enabled.
  • json_parse_flags_allow_trailing_comma - allow trailing commas in objects and arrays. For example, both [true,] and {"a" : null,} would be allowed with this option on.
  • json_parse_flags_allow_unquoted_keys - allow unquoted keys for objects. For example, {a : null} would be allowed with this option on.
  • json_parse_flags_allow_global_object - allow a global unbracketed object. For example, a : null, b : true, c : {} would be allowed with this option on.
  • json_parse_flags_allow_equals_in_object - allow objects to use '=' as well as ':' between key/value pairs. For example, {"a" = null, "b" : true} would be allowed with this option on.
  • json_parse_flags_allow_no_commas - allow that objects don't have to have comma separators between key/value pairs. For example, {"a" : null "b" : true} would be allowed with this option on.
  • json_parse_flags_allow_c_style_comments - allow c-style comments (// or /* */) to be ignored in the input JSON file.
  • json_parse_flags_deprecated - a deprecated option.
  • json_parse_flags_allow_location_information - allow location information to be tracked for where values are in the input JSON. Useful for alerting users to errors with precise location information pertaining to the original source. When this option is enabled, all json_value_s*'s can be casted to json_value_ex_s*, and the json_string_s* of json_object_element_s*'s name member can be casted to json_string_ex_s* to retrieve specific locations on all the values and keys. Note this option will increase the memory budget required for the DOM used to record the JSON.
  • json_parse_flags_allow_single_quoted_strings - allows strings to be in 'single quotes'.
  • json_parse_flags_allow_hexadecimal_numbers - allows hexadecimal numbers to be used 0x42.
  • json_parse_flags_allow_leading_plus_sign - allows a leading '+' sign on numbers +42.
  • json_parse_flags_allow_leading_or_trailing_decimal_point - allows decimal points to be lead or trailed by 0 digits .42 or 42..
  • json_parse_flags_allow_inf_and_nan - allows using infinity and NaN identifiers Infinity or NaN.
  • json_parse_flags_allow_multi_line_strings - allows strings to span multiple lines.
  • json_parse_flags_allow_simplified_json - allow simplified JSON to be parsed. Simplified JSON is an enabling of a set of other parsing options. See the Bitsquid blog introducing this here.
  • json_parse_flags_allow_json5 - allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing options. See the website defining this extension here.

Examples

Parsing with json_parse

Lets say we had the JSON string '{"a" : true, "b" : [false, null, "foo"]}'. To get to each part of the parsed JSON we'd do:

const char json[] = "{\"a\" : true, \"b\" : [false, null, \"foo\"]}";
struct json_value_s* root = json_parse(json, strlen(json));
assert(root->type == json_type_object);

struct json_object_s* object = (struct json_object_s*)root->payload;
assert(object->length == 2);

struct json_object_element_s* a = object->start;

struct json_string_s* a_name = a->name;
assert(0 == strcmp(a_name->string, "a"));
assert(a_name->string_size == strlen("a"));

struct json_value_s* a_value = a->value;
assert(a_value->type == json_type_true);
assert(a_value->payload == NULL);

struct json_object_element_s* b = a->next;
assert(b->next == NULL);

struct json_string_s* b_name = b->name;
assert(0 == strcmp(b_name->string, "b"));
assert(b_name->string_size == strlen("b"));

struct json_value_s* b_value = b->value;
assert(b_value->type == json_type_array);

struct json_array_s* array = (struct json_array_s*)b_value->payload;
assert(array->length == 3);

struct json_array_element_s* b_1st = array->start;

struct json_value_s* b_1st_value = b_1st->value;
assert(b_1st_value->type == json_type_false);
assert(b_1st_value->payload == NULL);

struct json_array_element_s* b_2nd = b_1st->next;

struct json_value_s* b_2nd_value = b_2nd->value;
assert(b_2nd_value->type == json_type_null);
assert(b_2nd_value->payload == NULL);

struct json_array_element_s* b_3rd = b_2nd->next;
assert(b_3rd->next == NULL);

struct json_value_s* b_3rd_value = b_3rd->value;
assert(b_3rd_value->type == json_type_string);

struct json_string_s* string = (struct json_string_s*)b_3rd_value->payload;
assert(0 == strcmp(string->string, "foo"));
assert(string->string_size == strlen("foo"));

/* Don't forget to free the one allocation! */
free(root);

Iterator Helpers

There are some functions that serve no purpose other than to make it nicer to iterate through the produced JSON DOM:

  • json_value_as_string - returns a value as a string, or null if it wasn't a string.
  • json_value_as_number - returns a value as a number, or null if it wasn't a number.
  • json_value_as_object - returns a value as an object, or null if it wasn't an object.
  • json_value_as_array - returns a value as an array, or null if it wasn't an array.
  • json_value_is_true - returns non-zero is a value was true, zero otherwise.
  • json_value_is_false - returns non-zero is a value was false, zero otherwise.
  • json_value_is_null - returns non-zero is a value was null, zero otherwise.

Lets look at the same example from above but using these helper iterators instead:

const char json[] = "{\"a\" : true, \"b\" : [false, null, \"foo\"]}";
struct json_value_s* root = json_parse(json, strlen(json));

struct json_object_s* object = json_value_as_object(root);
assert(object != NULL);
assert(object->length == 2);

struct json_object_element_s* a = object->start;

struct json_string_s* a_name = a->name;
assert(0 == strcmp(a_name->string, "a"));
assert(a_name->string_size == strlen("a"));

struct json_value_s* a_value = a->value;
assert(json_value_is_true(a_value));

struct json_object_element_s* b = a->next;
assert(b->next == NULL);

struct json_string_s* b_name = b->name;
assert(0 == strcmp(b_name->string, "b"));
assert(b_name->string_size == strlen("b"));

struct json_array_s* array = json_value_as_array(b->value);
assert(array->length == 3);

struct json_array_element_s* b_1st = array->start;

struct json_value_s* b_1st_value = b_1st->value;
assert(json_value_is_false(b_1st_value));

struct json_array_element_s* b_2nd = b_1st->next;

struct json_value_s* b_2nd_value = b_2nd->value;
assert(json_value_is_null(b_2nd_value));

struct json_array_element_s* b_3rd = b_2nd->next;
assert(b_3rd->next == NULL);

struct json_string_s* string = json_value_as_string(b_3rd->value);
assert(string != NULL);
assert(0 == strcmp(string->string, "foo"));
assert(string->string_size == strlen("foo"));

/* Don't forget to free the one allocation! */
free(root);

As you can see it makes iterating through the DOM a little more pleasant.

Extracting a Value from a DOM

If you want to extract a value from a DOM into a new allocation then json_extract_value and json_extract_value_ex are you friends. These functions let you take any value and its subtree from a DOM and clone it into a new allocation - either a single malloc or a user-provided allocation region.

const char json[] = "{\"foo\" : { \"bar\" : [123, false, null, true], \"haz\" : \"haha\" }}";
struct json_value_s* root = json_parse(json, strlen(json));
assert(root);

struct json_value_s* foo = json_value_as_object(root)->start->value;
assert(foo);

struct json_value_s* extracted = json_extract_value(foo);

/* We can free root now because we've got a new allocation for extracted! */
free(root);

assert(json_value_as_object(extracted));

/* Don't forget to free the one allocation! */
free(extracted);

Design

The json_parse function calls malloc once, and then slices up this single allocation to support all the weird and wonderful JSON structures you can imagine!

The structure of the data is always the JSON structs first (which encode the structure of the original JSON), followed by the data.

Todo

License

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to http://unlicense.org/

json.h's People

Contributors

codemonkey-uk avatar fireice-uk avatar kwikwag avatar ocornut avatar plzombie avatar sheredom avatar syntelos avatar timgates42 avatar xxxbxxx avatar zodf0055980 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

json.h's Issues

Buffer overflow in json_skip_c_style_comments

Hi, I use fuzzer find this overflow.

#include "json.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main(int argc, char *argv[]) { 
    char s[2] = "4e";
    json_parse_ex(s, 2, json_parse_flags_allow_json5, 0, 0, 0);
}

This is asan report.

==3377194==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffde23 at pc 0x555555588e2c bp 0x7fffffffdc10 sp 0x7fffffffdc00
READ of size 1 at 0x7fffffffde23 thread T0
    #0 0x555555588e2b in json_skip_c_style_comments json.h:547
    #1 0x55555558a26c in json_skip_all_skippables json.h:633
    #2 0x55555559ba6a in json_parse_ex json.h:2043
    #3 0x5555555a9e22 in main test_overflow.c:8
    #4 0x7ffff6a41082 in __libc_start_main ../csu/libc-start.c:308
    #5 0x55555558838d in _start (/home/yuan/json.h/a.out+0x3438d)

Address 0x7fffffffde23 is located in stack of thread T0 at offset 35 in frame
    #0 0x5555555a9d44 in main test_overflow.c:6

  This frame has 1 object(s):
    [32, 34) 's' (line 7) <== Memory access at offset 35 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow json.h:547 in json_skip_c_style_comments
Shadow bytes around the buggy address:
  0x10007fff7b70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7b80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7b90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
  0x10007fff7ba0: f1 f1 00 00 00 00 00 00 00 00 00 00 00 f3 f3 f3
  0x10007fff7bb0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007fff7bc0: f1 f1 f1 f1[02]f3 f3 f3 00 00 00 00 00 00 00 00
  0x10007fff7bd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7be0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7bf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007fff7c10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==3377194==ABORTING

JSON standard wants to escape /

The JSON standard allows for escaping the / character.
The software "Tiled" does this when you output JSON data, so using json.h to read those causes errors, because strings (like paths) look like "../../some/file".
Ignoring the '' in this case works fine

Heap Overflow in json_parse_string()

Heap Overflow in json_parse_string()

In json_parse_string(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of the vulnerability is when copy from src to data, the length of data(state.data_size) is not taken consideration of. Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0x5fd6d7d6d6247bff
=================================================================
==4928==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x613000000189 at pc 0x55b62d1096ee bp 0x7ffdc2d4b010 sp 0x7ffdc2d4b008
WRITE of size 1 at 0x613000000189 thread T0
    #0 0x55b62d1096ed in json_parse_string /home/hyrathon/Desktop/jsonh/./json.h:1540:29
    #1 0x55b62d10d6db in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1949:7
    #2 0x55b62d10c8f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #3 0x55b62d10d1c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #4 0x55b62d115c85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #5 0x55b62d12c192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #6 0x7fb9e4174d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #7 0x7fb9e4174e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #8 0x55b62d03a314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x613000000189 is located 0 bytes to the right of 329-byte region [0x613000000040,0x613000000189)
allocated by thread T0 here:
    #0 0x55b62d0bd15e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x55b62d114f15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x55b62d12c192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7fb9e4174d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1540:29 in json_parse_string
Shadow bytes around the buggy address:
  0x0c267fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c267fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c267fff8030: 00[01]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==4928==ABORTING

And the backtrace info from gdb:

#0  json_parse_string (state=0x7fffffffc700, string=0x613000000158) at ./json.h:1540
#1  0x000055555565a6dc in json_parse_value (state=0x7fffffffc700, is_global_object=0, value=0x613000000130) at ./json.h:1949
#2  0x00005555556598f1 in json_parse_object (state=0x7fffffffc700, is_global_object=1, object=0x613000000068) at ./json.h:1719
#3  0x000055555565a1c9 in json_parse_value (state=0x7fffffffc700, is_global_object=4, value=0x613000000040) at ./json.h:1940
#4  0x0000555555662c86 in json_parse_ex (src=0x7fffffffcb48, src_size=35, flags_bitset=6905944396334922751, alloc_func_ptr=0x0, user_data=0x0, result=0x7fffffffdbc0) at ./json.h:2129
#5  0x0000555555679193 in main (argc=2, argv=0x7fffffffde08) at fuzz.c:29
#6  0x00007ffff7ca3d90 in __libc_start_call_main (main=main@entry=0x555555678e30 <main>, argc=argc@entry=2, argv=argv@entry=0x7fffffffde08) at ../sysdeps/nptl/libc_start_call_main.h:58
#7  0x00007ffff7ca3e40 in __libc_start_main_impl (main=0x555555678e30 <main>, argc=2, argv=0x7fffffffde08, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffddf8) at ../csu/libc-start.c:392
#8  0x0000555555587315 in _start ()

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

Version: latest commit (bdcf2e1)

Also, you can always use the code below to reproduce the vulnerabilities I reported(and I won't attach it in each report for simplicity:

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "json.h"

#define BUFF_SIZE 0x1000

int main(int argc, char** argv){
    if(argc != 2){
        printf("Wrong number of arguments\n");
        exit(0);
    }

    char data[BUFF_SIZE];
    FILE *f = fopen(argv[1], "r");
    size_t size = fread(data, 1, BUFF_SIZE, f);

    if(size < sizeof(size_t)){
        // to short input, exit
        exit(0);
    }
    size_t flags = *((size_t *)data);

    printf("The flags is 0x%zx\n", flags);

    struct json_parse_result_s result;

    json_parse_ex(data + sizeof(size_t), size - sizeof(size_t), flags, NULL, NULL, &result);
}

clang fuzz.c -o san -O0 -g -fsanitize=address,undefined

crash-input.zip

Heap Overflow in json_parse_object()

Heap Overflow in json_parse_object()

In json_parse_object(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of this vulnerability is that the parser use value directly taken from state->dom as value_ex, while doesn't validate the bound of state->dom

    if (json_parse_flags_allow_location_information & flags_bitset) {
      struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom;
      state->dom += sizeof(struct json_value_ex_s);

      value_ex->offset = state->offset;
      value_ex->line_no = state->line_no;
      value_ex->row_no = state->offset - state->line_offset;

Similar situation can also be found at:

    if (json_parse_flags_allow_location_information & flags_bitset) {
      struct json_string_ex_s *string_ex =
          (struct json_string_ex_s *)state->dom;
      state->dom += sizeof(struct json_string_ex_s);

      string_ex->offset = state->offset;
      string_ex->line_no = state->line_no;
      string_ex->row_no = state->offset - state->line_offset;

Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0x5fd628d6d6247bff
=================================================================
==4959==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6130000001b8 at pc 0x55f27037a5df bp 0x7fff30738e90 sp 0x7fff30738e88
WRITE of size 8 at 0x6130000001b8 thread T0
    #0 0x55f27037a5de in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1709:24
    #1 0x55f27037b1c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #2 0x55f270383c85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #3 0x55f27039a192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #4 0x7f555df76d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #5 0x7f555df76e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #6 0x55f2702a8314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x6130000001b9 is located 0 bytes to the right of 377-byte region [0x613000000040,0x6130000001b9)
allocated by thread T0 here:
    #0 0x55f27032b15e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x55f270382f15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x55f27039a192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7f555df76d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1709:24 in json_parse_object
Shadow bytes around the buggy address:
  0x0c267fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c267fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c267fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c267fff8030: 00 00 00 00 00 00 00[01]fa fa fa fa fa fa fa fa
  0x0c267fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c267fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==4959==ABORTING

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

I also attach the input that can cause the other overflow I mentioned.

crash1.zip

ESP8266 Arduino

Wow this looks awesome :-)

Is there any way of getting this to work on an ESP8266/NodeMCU ?

I would like to serialize a struct and then do the reverse if possible.

Would be happy to try and feedback results.

Kind regards
Den

Minor overflow in json_skip_whitespace

I found a minor bug in json_skip_whitespace, however, I triggered this bug from the json_parse function with the following buffer as input: '\n\t\t\n\t\t\t\t"\x00'

The stack trace is as follows:
json_skip_whitespace at ./json.h:478
json_skip_all_skippables at ./json.h:610
json_parse_ex at ./json.h:1972
json_parse at ./json.h:2049

The bug happens because of the following reasons two function calls in json_parse_ex:
https://github.com/sheredom/json.h/blob/master/json.h#L1968-L1972

The first call to json_get_value_size results in a call to json_skip_all_skippables:

json.h/json.h

Lines 1249 to 1259 in eeb1c14

if (json_skip_all_skippables(state)) {
state->error = json_parse_error_premature_end_of_buffer;
return 1;
}
/* can cache offset now. */
offset = state->offset;
switch (src[offset]) {
case '"':
return json_get_string_size(state, 0);

This json_skip_all_skippables will set offset to be at the " character of the buffer (second to last).

Then, the call to json_get_string_size will result in offset being incremented by 2, in particular first on line:

json.h/json.h

Lines 656 to 658 in eeb1c14

/* skip leading '"' or '\''. */
offset++;

and again here:

json.h/json.h

Lines 776 to 779 in eeb1c14

/* skip trailing '"' or '\''. */
offset++;
/* add enough space to store the string. */

Then, when json_skip_all_skippables is called from inside the json_parse_ex, the following code:

json.h/json.h

Lines 604 to 610 in eeb1c14

do {
if (state->offset == size) {
state->error = json_parse_error_premature_end_of_buffer;
return 1;
}
did_consume = json_skip_whitespace(state);

will execute and offset will in fact be one above the size, which is why the overflow happens in json_skip_whitespace

json.h/json.h

Lines 470 to 478 in eeb1c14

json_weak int json_skip_whitespace(struct json_parse_state_s *state);
int json_skip_whitespace(struct json_parse_state_s *state) {
size_t offset = state->offset;
const size_t size = state->size;
const char *const src = state->src;
/* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and
* '\t'. */
switch (src[offset]) {

Line information in DOM

First of all, thanks for the great library!

I use some kind of schema to interpret the parsed DOM.
As this can fail, it would be nice to have line numbers to report to a user.

Looking at the code it seems it could be a matter of copying line number from the parsing state to json_value_s struct.

Do you mind increased json_value_s size?

Re-definition errors under GCC

Hey just wanted to say that using json.h has so far been an absolute pleasure. I had tried some other C/C++ JSON parsing libs and was not pleased. Came across yours and it's been just what I was looking for. Really admire the C style implementation too.

Have run into one issue though. I'm compiling under GCC and when I include the header in one implementation file it's just fine, but if I include it in more than one, GCC throws up an ocean of errors relating to it's global functions already being declared elsewhere.

I've tried some tests with modifying the json.h code, thinking maybe some pre-proc macros aren't having their intended effects, but have come up with nothing so far. My current temporary solution has been to have multiple headers for my different types of parsing I'm conducting, but put the implementations all in one .cpp file.

Am I missing something really obvious here? Can get you exact errors if that helps.

Thanks so much.

Error reporting

I started using this and I'll be wanting some more error reporting (being able to report line number/row and possibly type of errors). I think I can add them to the code but I was wondering what your priorities were in term of speed/functionalities trade-off and if they were good candidate for a merge.

For now I have just added line_no and line_start_offset to the json_parse_state_s structure which are only altered when encountering a \n. Byte count from beginning of line can be calculated using (offset-line_start_offset), it isn't a character count per se but already useful and the program can possibly output clang-style error reporting in context from that.

Since this is C my idea was to add an extra entry point, json_parse_ex() taking something like a json_parse_result_s* to output information such as the type of error and line number. Would you want such patch?

Heap buffer overflow in json_parse_value when use json_parse_flags_allow_inf_and_nan

Hi, I use fuzzer find this overflow.

#include "json.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char s[11] = "[Infinity0]";
    json_parse_ex(s, 11, json_parse_flags_allow_inf_and_nan, 0, 0, 0);
}

and

#include "json.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char s[6] = "[NaN0]";
    json_parse_ex(s, 6, json_parse_flags_allow_inf_and_nan, 0, 0, 0);
}

These can overflow in same part, this is asan report:

==915483==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x608000000070 at pc 0x55555555c4f4 bp 0x7fffffffdbf0 sp 0x7fffffffdbe0
WRITE of size 8 at 0x608000000070 thread T0
    #0 0x55555555c4f3 in json_parse_array json.h:1793
    #1 0x55555555d3ee in json_parse_value json.h:1948
    #2 0x55555555ea2b in json_parse_ex json.h:2115
    #3 0x555555565ddb in main overflow.c:8
    #4 0x7ffff73ae082 in __libc_start_main ../csu/libc-start.c:308
    #5 0x5555555552cd in _start (/home/yuan/json.h-or/crash+0x12cd)

0x608000000075 is located 0 bytes to the right of 85-byte region [0x608000000020,0x608000000075)
allocated by thread T0 here:
    #0 0x7ffff7689808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55555555e637 in json_parse_ex json.h:2074
    #2 0x555555565ddb in main overflow.c:8
    #3 0x7ffff73ae082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow json.h:1793 in json_parse_array
Shadow bytes around the buggy address:
  0x0c107fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c107fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c107fff8000: fa fa fa fa 00 00 00 00 00 00 00 00 00 00[05]fa
  0x0c107fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c107fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==915483==ABORTING

json.h under TrustInSoft CI

Hi,

I initiated the configuration of json.h on the new tool TrustInSoft CI. It's a source code analyzer, which analyzes execution paths (usually unit tests in your repo) to detect Undefined Behaviors along the way. Coverage includes all the defects that sanitizers ASAN, UBSan and MSan find, plus a large number of other (usually subtle) defects.

I've set up TrustInSoft CI on your test suite located in the test directory (using test/main.cpp as the entry point) and I've added tests from the JSON Parsing Test Suite (318 parsing tests + 22 transform tests = 340 tests total). Each test was run 4 times to emulate 4 different architectures: x86 32-bit, x86 64-bit, PPC 32-bit, and PPC 64-bit. You can check the results here.

The tool has proved the absence of Undefined Behaviors on all the analyzed execution paths. Since it relies on formal methods, it is able to detect the most subtle Undefined Behaviors, so these results are particularly impressive!

On the other hand I have noticed that 14 test cases from the JSON Parsing Test Suite give unexpected results: invalid JSON is parsed successfully.

Would you mind taking a look?

As I've set up TrustInSoft CI on my fork, one next step would be that you try it out in a continuous integration mode.

May I ask if this is something that you'd be interested in?

The setup consists in writing a configuration file. I can put my initial setup in a PR if you wish me to.

Thanks!

Buffer overflow in json_skip_whitespace

#include "json.h"
static char s[2] = "1e";
int main(void) { json_parse(s, 2); }
$ cc -g3 -fsanitize=address crash.c 
$ ./a.out 
ERROR: AddressSanitizer: global-buffer-overflow
READ of size 1 at 0x5561efd8d583 thread T0
    #0 json_skip_whitespace json.h/json.h:505
    #1 json_skip_all_skippables json.h/json.h:637
    #2 json_parse_ex json.h/json.h:2038
    #3 json_parse json.h/json.h:2115
    #4 main json.h/crash.c:3

Helping Parsing Array Json

Hello, I tried to parse this json format:
{ "exceptions": [], "category_rules": [{ "rule": { "is_blocked": false }, "name": "porn", "_id": "61cff41a54328f151d2f6a25" }, { "rule": { "is_blocked": false }, "name": "violent", "_id": "61cff41b54328f151d2f6a26" }, { "rule": { "is_blocked": false }, "name": "chat", "_id": "61cff41b54328f151d2f6a27" }, { "rule": { "is_blocked": true }, "name": "gambling", "_id": "61cff41b54328f151d2f6a28" }, { "rule": { "is_blocked": true }, "name": "dating", "_id": "61cff41b54328f151d2f6a29" }, { "rule": { "is_blocked": true }, "name": "game", "_id": "61cff41b54328f151d2f6a2a" }, { "rule": { "is_blocked": true }, "name": "drug", "_id": "61cff41b54328f151d2f6a2b" }] }

Im struggling with parsing these nested array elements. Can anyone help me to parse every single parts of this json ?

Heap buffer overflow in json_parse_array

Hi, I use fuzzer find this overflow.

#include "json.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char s1[40] = "{\"k\" : true/ \"b\" : [false, null, \"foo\"]}";
    json_parse_ex(s1, 40, 0x2ffff, 0, 0, 0);
}

this is asan report:

==1419822==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x615000000258 at pc 0x55555555c3ce bp 0x7fffffffdaa0 sp 0x7fffffffda90
WRITE of size 8 at 0x615000000258 thread T0
    #0 0x55555555c3cd in json_parse_array json.h:1782
    #1 0x55555555d517 in json_parse_value json.h:1958
    #2 0x55555555c067 in json_parse_object json.h:1715
    #3 0x55555555d265 in json_parse_value json.h:1936
    #4 0x55555555eb54 in json_parse_ex json.h:2125
    #5 0x555555565f5e in main overflow.c:8
    #6 0x7ffff73ae082 in __libc_start_main ../csu/libc-start.c:308
    #7 0x5555555552cd in _start (/home/yuan/json.h/crash+0x12cd)

0x615000000258 is located 0 bytes to the right of 472-byte region [0x615000000080,0x615000000258)
allocated by thread T0 here:
    #0 0x7ffff7689808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55555555e760 in json_parse_ex json.h:2084
    #2 0x555555565f5e in main overflow.c:8
    #3 0x7ffff73ae082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow json.h:1782 in json_parse_array
Shadow bytes around the buggy address:
  0x0c2a7fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff8000: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c2a7fff8030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c2a7fff8040: 00 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa
  0x0c2a7fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c2a7fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc

Test Case: json_write_pretty() with json string \u012b writes garbage

I've been using your library for a while - very nice. Thank you for the effort to build it!
I recently ran into an odd JSON string with a unicode value that looks like valid json to me (based on description of strings at www.json.org). 'json.c' parses it without error, but prints garbage due to the escape character (see program below for test case).

I ran this string through my vim json parser: PASS and pretty_prints correctly
I ran it through the parser at jsonlint.org: PASS and pretty_prints correctly
I run it through json.c: PASS... but it won't print correctly

Compiling on Ubuntu 12.04, g++ version 4.6.4

int main(void)
{
   const char payload[] = "{\"key1\":\"value1\", \"key2\":\"\\u012b\"}"; 

   printf("\nPrinting Payload:\n  %s\n\n", payload); 
   printf("\nProcessing Payload in JSON parser...\n");
   struct json_value_s *value = json_parse(payload, strlen(payload));
   void* tmp = json_write_pretty(value, "  ", "\n", 0); 

   if(tmp) {
       printf("\nPretty Payload:\n%s\n\n", (char*) tmp);
   }
   return 0; 
}

image

I think the solution is how to handle the escape character in 'json_parse_string()'...

I'm stumped!

Heap Overflow in json_parse_value()

Heap Overflow in json_parse_value()

In json_parse_value(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of this vulnerability is that the value transferred to json_parse_value() is not properly sanitized, and it is pointed to out-of-bound position.

Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0x5fd6d7d6d6247b16
=================================================================
==5035==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x611000000110 at pc 0x5615f43f0b7e bp 0x7ffc2be6fcd0 sp 0x7ffc2be6fcc8
WRITE of size 8 at 0x611000000110 thread T0
    #0 0x5615f43f0b7d in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1959:19
    #1 0x5615f43ef8f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #2 0x5615f43f01c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #3 0x5615f43f8c85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #4 0x5615f440f192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #5 0x7fee4e17bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #6 0x7fee4e17be3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #7 0x5615f431d314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x611000000111 is located 0 bytes to the right of 209-byte region [0x611000000040,0x611000000111)
allocated by thread T0 here:
    #0 0x5615f43a015e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x5615f43f7f15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x5615f440f192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7fee4e17bd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1959:19 in json_parse_value
Shadow bytes around the buggy address:
  0x0c227fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c227fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c227fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c227fff8020: 00 00[01]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c227fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5035==ABORTING

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

Version: latest commit (bdcf2e1)
crash-input.zip

[Feature Request] Lookup value via given key

Great work so far.

However one thing that would help tremendously is to have a (family of) utility function to look up values by a given key - right now I'd have to iterate through a linked list of the members of the json object, which makes the implemenation curve annoying. This has it's own performance implications ofc.

Parson has something like these:

JSON_Value* json_object_get_value(const JSON_Object* object, const char* name);
const char* json_object_get_string(const JSON_Object* object, const char* name);
JSON_Array* json_object_get_array(const JSON_Object* object, const char* name);
double        json_object_get_number(const JSON_Object* object, const char* name);

JSON improvement: support for // /* */ style comments?

I think another desirable (but totally optional) feature would be to add support for comments in the reader, possibly only // style if that is easier for the parser, or both // and /* */ style, so that would also be expressed as a flag as you've been adding flags to the entry point.

Should be fairly simple and lightweight, adding a test with '/' token and we can consume all the comment in that block. I'll look into that.

Stack Overflow in json_get_value_size()

Environment

Ubuntu22.04 LST
Linux 5.15.90.1-microsoft-standard-WSL2 #1 SMP Fri Jan 27 02:56:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

json.h commit: 06aa578

Vulnerability

As we all know, each function call in the process space will establish and maintain the stack frame of this function call on the stack space of the current thread, but the problem is that the stack space of the process is limited. If a program keeps If the recursive function is called, the stack space of the process will be consumed quickly. If the call is deep, the stack space will be exhausted, and it will evolve into a stack overflow formed by infinite recursive function calls, causing the program to crash. In the json_get_value_size function , there is an appeal vulnerability.

Specifically, in the json_get_value_size function, we can find a recursively nested branch, and this branch can be easily triggered. The figure below describes this recursive call chain, and we can continuously nest object and array objects in the json text to repeatedly call among the three, forming infinite recursion until the stack space is exhausted.
In short,json_get_value_size ->json_get_array_size ->json_get_value_size can be continuously formed... When such a recursive call is deep enough, the stack space will be exhausted, and we only need continuous nested arrays in the json file, which is The exploitation of the attack is very simple.
image

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "json.h"

int json_get_value_size(struct json_parse_state_s *state,
                        int is_global_object) {
    const size_t flags_bitset = state->flags_bitset;
    const char *const src = state->src;
    size_t offset;
    const size_t size = state->size;

    // .... skip some code

      /* can cache offset now. */
      offset = state->offset;

      switch (src[offset]) {
        case '"':
            return json_get_string_size(state, 0);
        case '\'':
            if (json_parse_flags_allow_single_quoted_strings & flags_bitset) {
          return json_get_string_size(state, 0);
      } else {
          /* invalid value! */
          state->error = json_parse_error_invalid_value;
          return 1;
      }
        case '{':
            return json_get_object_size(state, /* is_global_object = */ 0);
        case '[':
            return json_get_array_size(state);
        case '-':
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            return json_get_number_size(state);
        case '+':
            if (json_parse_flags_allow_leading_plus_sign & flags_bitset) {
          		return json_get_number_size(state);
              } else {
                  /* invalid value! */
                  state->error = json_parse_error_invalid_number_format;
                  return 1;
              }
                case '.':
                    if (json_parse_flags_allow_leading_or_trailing_decimal_point &
                  flags_bitset) {
                  return json_get_number_size(state);
              } else {
                  /* invalid value! */
                  state->error = json_parse_error_invalid_number_format;
                  return 1;
              }
        default:
            // ....
    }
  }
}

This is the report given by SanitizerAddress when I trigger this vulnerability with the following code. You can use the crash file I provided to report and reproduce this vulnerability.
plz use gcc main.c -o main -fsanitize=address to compile.

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "json.h"

long get_file_size(FILE *stream)
{
	long file_size = -1;
	long cur_offset = ftell(stream);	
	if (cur_offset == -1) {
		printf("ftell failed :%s\n", strerror(errno));
		return -1;
	}
	if (fseek(stream, 0, SEEK_END) != 0) {	
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	file_size = ftell(stream);
	if (file_size == -1) {
		printf("ftell failed :%s\n", strerror(errno));
	}
	if (fseek(stream, cur_offset, SEEK_SET) != 0) {	
		printf("fseek failed: %s\n", strerror(errno));
		return -1;
	}
	return file_size;
}

int main(int argc, char const *argv[])
{   
    if(argc != 2){
        printf("Wrong number of arguments\n");
        return -1;
    }

    FILE* fstream = NULL;
    fstream = fopen(argv[1], "r");
    if (fstream == NULL) {
        printf("fopen error");
        return -1;
    }

    int file_size = get_file_size(fstream);
    
    unsigned char* buf = malloc(file_size + 1);
    memset(buf, 0, file_size + 1);

    fread(buf, 1, file_size, fstream);


    json_parse(buf, file_size);


    return 0;
}
cxing@DESKTOP:~/fuzz/fuzz-json/json.h$ ./main_fsanitize crash_1
AddressSanitizer:DEADLYSIGNAL
=================================================================
==2083807==ERROR: AddressSanitizer: stack-overflow on address 0x7ffd1eae5f98 (pc 0x55a0ffa632b0 bp 0x7ffd1eae6060 sp 0x7ffd1eae5f90 T0)
    #0 0x55a0ffa632b0 in json_get_string_size /home/cxing/fuzz/fuzz-json/json.h/json.h:677
    #1 0x55a0ffa644eb in json_get_key_size /home/cxing/fuzz/fuzz-json/json.h/json.h:906
    #2 0x55a0ffa64a3e in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:996
    #3 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #4 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #5 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #6 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #7 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #8 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #9 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #10 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #11 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #12 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #13 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #14 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #15 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #16 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #17 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #18 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #19 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #20 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #21 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #22 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #23 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #24 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #25 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #26 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #27 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #28 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #29 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #30 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #31 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #32 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #33 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #34 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #35 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #36 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #37 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #38 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #39 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #40 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #41 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #42 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #43 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #44 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #45 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #46 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #47 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #48 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #49 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #50 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #51 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #52 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #53 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #54 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #55 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #56 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #57 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #58 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #59 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #60 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #61 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #62 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #63 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #64 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #65 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #66 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #67 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #68 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #69 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #70 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #71 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #72 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #73 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #74 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #75 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #76 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #77 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #78 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #79 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #80 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #81 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #82 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #83 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #84 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #85 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #86 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #87 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #88 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #89 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #90 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #91 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #92 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #93 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #94 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #95 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #96 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #97 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #98 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #99 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #100 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #101 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #102 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #103 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #104 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #105 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #106 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #107 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #108 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #109 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #110 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #111 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #112 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #113 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #114 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #115 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #116 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #117 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #118 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #119 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #120 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #121 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #122 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #123 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #124 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #125 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #126 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #127 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #128 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #129 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #130 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #131 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #132 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #133 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #134 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #135 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #136 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #137 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #138 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #139 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #140 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #141 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #142 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #143 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #144 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #145 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #146 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #147 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #148 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #149 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #150 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #151 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #152 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #153 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #154 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #155 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #156 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #157 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #158 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #159 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #160 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #161 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #162 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #163 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #164 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #165 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #166 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #167 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #168 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #169 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #170 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #171 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #172 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #173 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #174 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #175 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #176 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #177 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #178 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #179 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #180 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #181 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #182 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #183 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #184 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #185 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #186 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #187 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #188 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #189 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #190 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #191 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #192 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #193 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #194 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #195 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #196 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #197 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #198 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #199 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #200 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #201 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #202 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #203 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #204 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #205 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #206 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #207 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #208 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #209 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #210 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #211 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #212 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #213 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #214 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #215 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #216 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #217 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #218 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #219 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #220 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #221 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #222 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #223 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #224 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #225 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #226 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #227 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #228 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #229 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #230 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #231 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #232 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #233 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #234 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #235 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #236 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #237 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #238 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #239 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #240 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #241 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #242 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #243 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #244 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105
    #245 0x55a0ffa66d2c in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1383
    #246 0x55a0ffa64cd6 in json_get_object_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1028
    #247 0x55a0ffa66d1b in json_get_value_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1381
    #248 0x55a0ffa6522c in json_get_array_size /home/cxing/fuzz/fuzz-json/json.h/json.h:1105

SUMMARY: AddressSanitizer: stack-overflow /home/cxing/fuzz/fuzz-json/json.h/json.h:677 in json_get_string_size
==2083807==ABORTING

Urgent fix suggestion

A simple and effective way to deal with infinite recursion is to record the depth of function recursion in recursively nested functions, and terminate the parsing when the depth reaches a dangerous threshold.

crash_1.zip

Single file distribution

Would it be possible to append the C file into the header file and wrap it in #if !defined(SHEREDOM_JSON_HEADER) to streamline unity builds? (or alternatively #if defined(SHEREDOM_JSON_IMPLEMENTATION); I prefer the first style in contrast to stb-style for unity builds, but the second may be less confusing for stb users)

Heap Overflow in json_parse_key()

Heap Overflow in json_parse_key()

In json_parse_key(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of this vulnerability is that the parameter string transferred to json_parse_key() is not properly sanitized, and it is pointed to out-of-bound position assigned by upper layer function.

Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0xd6d7d6d6247bff7f
=================================================================
==5286==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61f000000c58 at pc 0x5651b3173086 bp 0x7ffcafde8e10 sp 0x7ffcafde8e08
WRITE of size 8 at 0x61f000000c58 thread T0
    #0 0x5651b3173085 in json_parse_key /home/hyrathon/Desktop/jsonh/./json.h:1576:22
    #1 0x5651b3174e8b in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1694:11
    #2 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #3 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #4 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #5 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #6 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #7 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #8 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #9 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #10 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #11 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #12 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #13 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #14 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #15 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #16 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #17 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #18 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #19 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #20 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #21 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #22 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #23 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #24 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #25 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #26 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #27 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #28 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #29 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #30 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #31 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #32 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #33 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #34 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #35 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #36 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #37 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #38 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #39 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #40 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #41 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #42 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #43 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #44 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #45 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #46 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #47 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #48 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #49 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #50 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #51 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #52 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #53 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #54 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #55 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #56 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #57 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #58 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #59 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #60 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #61 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #62 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #63 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #64 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #65 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #66 0x5651b3176ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #67 0x5651b317ad4b in json_parse_array /home/hyrathon/Desktop/jsonh/./json.h:1809:5
    #68 0x5651b3176ed5 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1962:7
    #69 0x5651b31758f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #70 0x5651b31761c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #71 0x5651b317ec85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #72 0x5651b3195192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #73 0x7f1945b88d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #74 0x7f1945b88e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #75 0x5651b30a3314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x61f000000c58 is located 14 bytes to the right of 3018-byte region [0x61f000000080,0x61f000000c4a)
allocated by thread T0 here:
    #0 0x5651b312615e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x5651b317df15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x5651b3195192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7f1945b88d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1576:22 in json_parse_key
Shadow bytes around the buggy address:
  0x0c3e7fff8130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3e7fff8140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3e7fff8150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3e7fff8160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c3e7fff8170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c3e7fff8180: 00 00 00 00 00 00 00 00 00 02 fa[fa]fa fa fa fa
  0x0c3e7fff8190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3e7fff81a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3e7fff81b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3e7fff81c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c3e7fff81d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5286==ABORTING

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

Version: latest commit (bdcf2e1)

Also I will attach another input that can trigger similar bug in json_parse_key().
crash-another.zip

Buffer overflow in json_get_number_size

#include "json.h"
static char s[2] = "0.";
int main(void) { json_parse(s, 2); }
$ cc -g3 -fsanitize=address crash.c 
$ ./a.out
ERROR: AddressSanitizer: global-buffer-overflow
READ of size 1 at 0x5630032b30e2 thread T0
    #0 json_get_number_size json.h:1223
    #1 json_get_value_size json.h:1352
    #2 json_parse_ex json.h:2039
    #3 json_parse json.h:2120
    #4 main crash.c:3

By the way, I've been finding these as a result of fuzzing a project that uses json.h. Here's a fuzz target to find defects directly in json.h.

#include <stdio.h>
#include <stdlib.h>
#include "json.h"

int main(void)
{
    int n = 1 << 12;
    void *buf = malloc(n);
    int len = fread(buf, 1, n, stdin);
    buf = realloc(buf, len);
    void *volatile sink = json_parse(buf, len);
}

Usage:

$ afl-gcc -m32 -g3 -fsanitize=address,undefined fuzz.c
$ mkdir in
$ echo {} >in/a
$ afl-fuzz -m800 -iin -oout ./a.out

Iterate over array linked list

I've tried to use the lib for my Windows Driver json parsing requirements. Everything is fine and working but I can't figure out how I can iterate through array object to get all values. My Json:
{"app_blocks":["firefox","cmd","chrome"]}

Here is my code:
` struct json_value_s* root = json_parse(buffer, strlen(buffer));

struct json_object_s* object = (struct json_object_s*)root->payload;

struct json_object_element_s* firstObject = object->start;
struct json_value_s* app_array_value = firstObject->value;


struct json_array_s* array_value = (struct json_array_s*)app_array_value->payload;

struct json_array_element_s* b_1st = array_value->start;
struct json_value_s* b_1st_value = b_1st->value;
struct json_string_s* current_value = b_1st_value->payload;

`

How I can iterate through this linked list to insert it to another list (vector or normal array, etc) ?

Ideas for some large changes and some inline macro sugar.

Hi,
I'm interested to hear your option on those changes. Let's start with the large ones:

  • Right now arrays are O(n), this is unnecessary. How about changing them to O(1) with a block of array elements after array data to make parsing easier. So [ARRAY OBJECT] ............ [ARRAY_ELEMENT_1][ARRAY_ELEMENT_2] etc.
  • Having separate json_value_s and type structures in the DOM increases its size and makes us jump around with pointers and uglify our code with casts. Why not have a union of those structures inside of json_value_s? Useful bonus here is that we can have an immediate value bool instead of one hacked from types.
  • Object and array access functions and some useful inlines for type checking and quick access to the value via relevant c library functions like atoll or atof.

Let me know what you think

Way to copy the JSON object

This is an excellent library but it lacks one useful feature and that is a way to make a copy of the tree you're working with. It's quite beneficial to want to take a copy of the tree or subtree you index for the purposes of storing and passing it off to other systems. The lifetime management gets quite hairy to the point where you're better off just reference counting the root of the tree every time you take a pointer off it. This should be trivial to support since only one call to malloc is made and you can more or less just memcpy things around, but it would be nice if you just copy the subtree if you're indexing.

Heap Overflow in json_parse_number()

Heap Overflow in json_parse_number()

In json_parse_number(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of this vulnerability is that the parameter number transferred to json_parse_number() is not properly sanitized, and it is pointed to out-of-bound position assigned by upper layer function.

Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0x5fd6d79cd6247bff
=================================================================
==5128==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001c0 at pc 0x560f532c646f bp 0x7ffc97871f80 sp 0x7ffc97871f78
WRITE of size 8 at 0x6140000001c0 thread T0
    #0 0x560f532c646e in json_parse_number /home/hyrathon/Desktop/jsonh/./json.h:1839:18
    #1 0x560f532c22d1 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1980:7
    #2 0x560f532c08f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #3 0x560f532c11c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #4 0x560f532c9c85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #5 0x560f532e0192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #6 0x7f65c9a20d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #7 0x7f65c9a20e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #8 0x560f531ee314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x6140000001c4 is located 0 bytes to the right of 388-byte region [0x614000000040,0x6140000001c4)
allocated by thread T0 here:
    #0 0x560f5327115e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x560f532c8f15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x560f532e0192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7f65c9a20d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1839:18 in json_parse_number
Shadow bytes around the buggy address:
  0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
  0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c287fff8030: 00 00 00 00 00 00 00 00[04]fa fa fa fa fa fa fa
  0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c287fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5128==ABORTING

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

Version: latest commit (bdcf2e1)

crash-input.zip

Convert a json object into json string

I am not sure if it goes out of scope for this library but I have a use case where I want to parse an object and return value (object type) of a key.

eg. -

if json string is - "{\"a\":11,\"b\":{\"c\":22,\"d\":\"1234\"}}"

then I want to return "{\"c\":22,\"d\":\"1234\"}"

Let me know if it is possible. Thanks

json.h parser allocates memory it doesn't use

Hello,

The parser seems to overestimate the amount of memory needed for the DOM. On a x64 build it will be exactly 32 bytes per every JSON object's key pair. It seems to be happening because both json_get_object_size and the helper functions json_get_key_size and json_get_value_size add to the dom_size. Not sure which addition (in the functions, or at the end of json_get_object_size) is the correct one.

Example input '{"a":"b"}', no parser flags.
Calculated dom_size = 136
data_size = 4
total_size = 140
Actual DOM end is at offset 104, memory between 104 and 136 is not written to.

Another Heap OOB in json_parse_object()

Another Heap OOB in json_parse_object()

In json_parse_object(), there is a heap out-of-bound write. The bug can be triggered with an invocation of json_parse_ex() with specific flags combination. The root cause of this vulnerability is that the parameter object transferred to json_parse_obect() is not properly sanitized, and it is pointed to out-of-bound position assigned by upper layer function.

Here is the output of Google ASAN when the vulnerability is triggerred:

The flags is 0x5fd6d7d6d6247bdf
=================================================================
==5354==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61a000000550 at pc 0x557f8e3323e7 bp 0x7ffd29216ab0 sp 0x7ffd29216aa8
WRITE of size 8 at 0x61a000000550 thread T0
    #0 0x557f8e3323e6 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1670:21
    #1 0x557f8e334ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #2 0x557f8e3338f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #3 0x557f8e334ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #4 0x557f8e3338f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #5 0x557f8e334ad9 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1955:7
    #6 0x557f8e338d4b in json_parse_array /home/hyrathon/Desktop/jsonh/./json.h:1809:5
    #7 0x557f8e334ed5 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1962:7
    #8 0x557f8e3338f0 in json_parse_object /home/hyrathon/Desktop/jsonh/./json.h:1719:5
    #9 0x557f8e3341c8 in json_parse_value /home/hyrathon/Desktop/jsonh/./json.h:1940:5
    #10 0x557f8e33cc85 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2129:3
    #11 0x557f8e353192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #12 0x7fae53ff9d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #13 0x7fae53ff9e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #14 0x557f8e261314 in _start (/home/hyrathon/Desktop/jsonh/san+0x33314) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)

0x61a000000550 is located 0 bytes to the right of 1232-byte region [0x61a000000080,0x61a000000550)
allocated by thread T0 here:
    #0 0x557f8e2e415e in __interceptor_malloc (/home/hyrathon/Desktop/jsonh/san+0xb615e) (BuildId: a0e983e1c9c38a8763824dc5481f79d22fe8c82a)
    #1 0x557f8e33bf15 in json_parse_ex /home/hyrathon/Desktop/jsonh/./json.h:2088:18
    #2 0x557f8e353192 in main /home/hyrathon/Desktop/jsonh/fuzz.c:29:5
    #3 0x7fae53ff9d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/hyrathon/Desktop/jsonh/./json.h:1670:21 in json_parse_object
Shadow bytes around the buggy address:
  0x0c347fff8050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c347fff8060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c347fff8070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c347fff8080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c347fff8090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c347fff80a0: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
  0x0c347fff80b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c347fff80c0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c347fff80d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c347fff80e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c347fff80f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==5354==ABORTING

The input to trigger this bug is attached. The first 8 bytes are the flags for json_parse_ex() and the rest is the content(src). Please use address sanitizer to reproduce the bug, as non-crash overflow is hard to detect or locate without shadow memory.

Version: latest commit (bdcf2e1)
crash-input.zip

This causes the parser to hang

const char* str = R"fs(
    "groovy": "yep"
    asdf: 42
)fs";

printf( "Preparing to parse\n" );
json_value_s* value = json_parse_ex(str, strlen(str), json_parse_flags_allow_simplified_json, 0);   
printf( "Never get's here...\n" );

return 0;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.