Browse Source

Tests that try to show that can't get/add keys with embedded null characters

pull/715/head
HexTheDragon 4 years ago
parent
commit
03f897e574
10 changed files with 408 additions and 11 deletions
  1. +5
    -2
      json-c.sym
  2. +28
    -2
      json_object.c
  3. +97
    -7
      json_object.h
  4. +2
    -0
      tests/CMakeLists.txt
  5. +123
    -0
      tests/test_null_keys_add.c
  6. +6
    -0
      tests/test_null_keys_add.expected
  7. +1
    -0
      tests/test_null_keys_add.test
  8. +139
    -0
      tests/test_null_keys_get.c
  9. +6
    -0
      tests/test_null_keys_get.expected
  10. +1
    -0
      tests/test_null_keys_get.test

+ 5
- 2
json-c.sym View File

@@ -165,6 +165,9 @@ JSONC_0.15 {
} JSONC_0.14; } JSONC_0.14;


JSONC_0.16 { JSONC_0.16 {
# global:
# ...new symbols here...
global:
json_object_object_add_len;
json_object_object_add_ex_len;
json_object_object_get_len;
json_object_object_get_ex_len;
} JSONC_0.15; } JSONC_0.15;

+ 28
- 2
json_object.c View File

@@ -582,6 +582,12 @@ struct lh_table *json_object_get_object(const struct json_object *jso)


int json_object_object_add_ex(struct json_object *jso, const char *const key, int json_object_object_add_ex(struct json_object *jso, const char *const key,
struct json_object *const val, const unsigned opts) struct json_object *const val, const unsigned opts)
{
return json_object_object_add_ex_len(jso, key, strlen(key), val, opts);
}

int json_object_object_add_ex_len(struct json_object *jso, const char *const key, const int len,
struct json_object *const val, const unsigned opts)
{ {
struct json_object *existing_value = NULL; struct json_object *existing_value = NULL;
struct lh_entry *existing_entry; struct lh_entry *existing_entry;
@@ -619,7 +625,13 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key,


int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val)
{ {
return json_object_object_add_ex(jso, key, val, 0);
return json_object_object_add_ex_len(jso, key, strlen(key), val, 0);
}

int json_object_object_add_len(struct json_object *jso, const char *key, const int len,
struct json_object *val)
{
return json_object_object_add_ex_len(jso, key, len, val, 0);
} }


int json_object_object_length(const struct json_object *jso) int json_object_object_length(const struct json_object *jso)
@@ -636,12 +648,26 @@ size_t json_c_object_sizeof(void)
struct json_object *json_object_object_get(const struct json_object *jso, const char *key) struct json_object *json_object_object_get(const struct json_object *jso, const char *key)
{ {
struct json_object *result = NULL; struct json_object *result = NULL;
json_object_object_get_ex(jso, key, &result);
json_object_object_get_ex_len(jso, key, strlen(key), &result);
return result;
}

struct json_object *json_object_object_get_len(const struct json_object *jso, const char *key,
const int len)
{
struct json_object *result = NULL;
json_object_object_get_ex_len(jso, key, len, &result);
return result; return result;
} }


json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, json_bool json_object_object_get_ex(const struct json_object *jso, const char *key,
struct json_object **value) struct json_object **value)
{
return json_object_object_get_ex_len(jso, key, strlen(key), value);
}

json_bool json_object_object_get_ex_len(const struct json_object *jso, const char *key,
const int len, struct json_object **value)
{ {
if (value != NULL) if (value != NULL)
*value = NULL; *value = NULL;


+ 97
- 7
json_object.h View File

@@ -385,6 +385,36 @@ JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void));
JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key,
struct json_object *val); struct json_object *val);


/** Add an object field to a json_object of type json_type_object
*
* The reference count of `val` will *not* be incremented, in effect
* transferring ownership that object to `obj`, and thus `val` will be
* freed when `obj` is. (i.e. through `json_object_put(obj)`)
*
* If you want to retain a reference to the added object, independent
* of the lifetime of obj, you must increment the refcount with
* `json_object_get(val)` (and later release it with json_object_put()).
*
* Since ownership transfers to `obj`, you must make sure
* that you do in fact have ownership over `val`. For instance,
* json_object_new_object() will give you ownership until you transfer it,
* whereas json_object_object_get() does not.
*
* Any previous object stored under `key` in `obj` will have its refcount
* decremented, and be freed normally if that drops to zero.
*
* @param obj the json_object instance
* @param key the object field name (a private copy will be duplicated),
* which is not terminated by a NULL ( @c '\0' ) character
* @param len the length of @p key
* @param val a json_object or NULL member to associate with the given field
*
* @return On success, @c 0 is returned.
* On error, a negative value is returned.
*/
JSON_EXPORT int json_object_object_add_len(struct json_object *obj, const char *key, const int len,
struct json_object *val);

/** Add an object field to a json_object of type json_type_object /** Add an object field to a json_object of type json_type_object
* *
* The semantics are identical to json_object_object_add, except that an * The semantics are identical to json_object_object_add, except that an
@@ -397,13 +427,51 @@ JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key,
* @param val a json_object or NULL member to associate with the given field * @param val a json_object or NULL member to associate with the given field
* @param opts process-modifying options. To specify multiple options, use * @param opts process-modifying options. To specify multiple options, use
* (OPT1|OPT2) * (OPT1|OPT2)
*
* @return On success, @c 0 is returned.
* On error, a negative value is returned.
*/ */
JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key,
struct json_object *const val, const unsigned opts); struct json_object *const val, const unsigned opts);


/** Add an object field to a json_object of type json_type_object
*
* The semantics are identical to json_object_object_add, except that an
* additional flag fields gives you more control over some detail aspects
* of processing. See the description of JSON_C_OBJECT_ADD_* flags for more
* details.
*
* @param obj the json_object instance
* @param key the object field name (a private copy will be duplicated),
* which is not terminated by a NULL ( @c '\0' ) character
* @param len the length of @p key
* @param val a json_object or NULL member to associate with the given field
* @param opts process-modifying options. To specify multiple options, use
* (OPT1|OPT2)
*
* @return On success, @c 0 is returned.
* On error, a negative value is returned.
*/
JSON_EXPORT int json_object_object_add_ex_len(struct json_object *obj, const char *const key,
const int len, struct json_object *const val,
const unsigned opts);

/** Get the json_object associate with a given object field. /** Get the json_object associate with a given object field.
* Deprecated/discouraged: used json_object_object_get_ex instead. * Deprecated/discouraged: used json_object_object_get_ex instead.
* *
* This functions exactly like calling
* @code json_object_object_get_len(obj, key, strlen(key)) @endcode
*
* @param obj the json_object instance
* @param key the object field name
* @returns the json_object associated with the given field name
* @see json_object_object_get_len()
*/
JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj,
const char *key);
/** Get the json_object associate with a given object field.
* Deprecated/discouraged: used json_object_object_get_ex_len instead.
*
* This returns NULL if the field is found but its value is null, or if * This returns NULL if the field is found but its value is null, or if
* the field is not found, or if obj is not a json_type_object. If you * the field is not found, or if obj is not a json_type_object. If you
* need to distinguish between these cases, use json_object_object_get_ex(). * need to distinguish between these cases, use json_object_object_get_ex().
@@ -419,11 +487,31 @@ JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *c
* or transfer ownership to prevent a memory leak). * or transfer ownership to prevent a memory leak).
* *
* @param obj the json_object instance * @param obj the json_object instance
* @param key the object field name
* @param key the object field name,
* which is not terminated by a NULL ( @c '\0' ) character
* @param len the length of @p key
* @returns the json_object associated with the given field name * @returns the json_object associated with the given field name
*/ */
JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj,
const char *key);

JSON_EXPORT struct json_object *json_object_object_get_len(const struct json_object *obj,
const char *key, const int len);

/** Get the json_object associated with a given object field.
*
* This functions exactly like calling
* @code json_object_object_get_ex_len(obj, key, strlen(key), value) @endcode
*
* @param obj the json_object instance
* @param key the object field name
* @param value a pointer where to store a reference to the json_object
* associated with the given field name.
* \n
* It is safe to pass a NULL value.
* @returns whether or not the key exists
* @see json_object_object_get_ex_len()
*/
JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key,
struct json_object **value);


/** Get the json_object associated with a given object field. /** Get the json_object associated with a given object field.
* *
@@ -436,15 +524,17 @@ JSON_EXPORT struct json_object *json_object_object_get(const struct json_object
* than the owning parent (obj). Ownership of value is retained by obj. * than the owning parent (obj). Ownership of value is retained by obj.
* *
* @param obj the json_object instance * @param obj the json_object instance
* @param key the object field name
* @param key the object field name,
* which is not terminated by a NULL ( @c '\0' ) character
* @param len the length of @p key
* @param value a pointer where to store a reference to the json_object * @param value a pointer where to store a reference to the json_object
* associated with the given field name. * associated with the given field name.
*
* \n
* It is safe to pass a NULL value. * It is safe to pass a NULL value.
* @returns whether or not the key exists * @returns whether or not the key exists
*/ */
JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key,
struct json_object **value);
JSON_EXPORT json_bool json_object_object_get_ex_len(const struct json_object *obj, const char *key,
const int len, struct json_object **value);


/** Delete the given json_object field /** Delete the given json_object field
* *


+ 2
- 0
tests/CMakeLists.txt View File

@@ -26,6 +26,8 @@ set(ALL_TEST_NAMES
test_int_add test_int_add
test_locale test_locale
test_null test_null
test_null_keys_add
test_null_keys_get
test_parse test_parse
test_parse_int64 test_parse_int64
test_printbuf test_printbuf


+ 123
- 0
tests/test_null_keys_add.c View File

@@ -0,0 +1,123 @@
/*
* Tests if binary strings are supported.
*/

#include "config.h"
#include <stdio.h>
#include <string.h>

#include "json_inttypes.h"
#include "json_object.h"
#include "json_tokener.h"

int main(void)
{
/* this test has embedded null characters in the key and value.
* check that it's still included after deserializing. */
const char *input = "{ \"foo\": 14.5, \"bar\": [] }";
const char *foo_key = "foo";
const char *foo_value = "14.5";
const char *bar_key = "bar";

const char *toadd_key = "foo\0bar";
const int toadd_key_len = 7;
const char *toadd_key_printable = "foo\\0bar";
const char *toadd_value = "qwerty\0asdf";
const int toadd_value_len = 12;
const char *toadd_value_printable = "qwerty\\0asdf";

struct json_object *parsed = json_tokener_parse(input);

printf("Parsed input: %s\n", input);
printf("Result is ");
if (parsed == NULL)
{
printf("NULL (error!)\n");
return 1; // Error somewhere
}
else if (!json_object_is_type(parsed, json_type_object))
{
printf("not `json_type_object` (error!)\n");
return 1; // Error somewhere
}
else
{
printf("`json_type_object`\n");
}

// Check nothing odd happened in parsing
if (json_object_object_length(parsed) != 2)
{
printf("Should contain two fields (has %d) (error!)",
json_object_object_length(parsed));
return 1;
}
json_bool key_present = json_object_object_get_ex(parsed, foo_key, NULL);
if (!key_present)
{
printf("Should contain key \"%s\", but does not (error!)", foo_key);
return 1;
}
key_present = json_object_object_get_ex(parsed, bar_key, NULL);
if (!key_present)
{
printf("Should contain key \"%s\", but does not (error!)", bar_key);
return 1;
}

// Add the new key
struct json_object *new_str = json_object_new_string_len(toadd_value, toadd_value_len);
if (json_object_object_add_ex(parsed, toadd_key, new_str,
JSON_C_OBJECT_ADD_KEY_IS_NEW |
JSON_C_OBJECT_KEY_IS_CONSTANT) != 0)
{
printf("An error occured adding the key \"%s\" (error!)\n", toadd_key_printable);
return 1;
}

// Check the new key was actually added
if (json_object_object_length(parsed) != 3)
{
printf("Should contain three fields after adding new key (has %d) (error!)",
json_object_object_length(parsed));
return 1;
}
else if (json_object_object_get_len(parsed, toadd_key, toadd_key_len) != new_str)
{
printf("Have three keys, but don't have the right value in \"%s\" (error!)\n",
toadd_key_printable);
return 1;
}
else
{
printf("Added the key \"%s\" successfully\n", toadd_key_printable);
}

// Check the previous keys are still the same present
struct json_object *foo = json_object_object_get(parsed, foo_key);
if (!json_object_is_type(foo, json_type_double))
{
printf("Key \"%s\" should be `json_type_double` (%d) but was %d (error!)\n",
foo_key, (int)json_type_double, json_object_get_type(foo));
return 1;
}
else
{
printf("Key \"%s\" is still the same\n", foo_key);
}
struct json_object *bar = json_object_object_get(parsed, bar_key);
if (!json_object_is_type(foo, json_type_array))
{
printf("Key \"%s\" should be `json_type_array` (%d) but was %d (error!)\n", bar_key,
(int)json_type_array, json_object_get_type(foo));
return 1;
}
else
{
printf("Key \"%s\" is still the same\n", bar_key);
}

json_object_put(parsed);
printf("PASS\n");
return 0;
}

+ 6
- 0
tests/test_null_keys_add.expected View File

@@ -0,0 +1,6 @@
Parsed input: { "foo": 14.5, "bar": [] }
Result is `json_type_object`
Added the key "foo\0bar" successfully
Key "foo" is still the same
Key "bar" is still the same
PASS

+ 1
- 0
tests/test_null_keys_add.test View File

@@ -0,0 +1 @@
test_basic.test

+ 139
- 0
tests/test_null_keys_get.c View File

@@ -0,0 +1,139 @@
/*
* Tests if binary strings are supported.
*/

#include "config.h"
#include <stdio.h>
#include <string.h>

#include "json_inttypes.h"
#include "json_object.h"
#include "json_tokener.h"

int main(void)
{
/* this test has embedded null characters in the key and value.
* check that it's still included after deserializing. */
const char *input = "{ \"foo\\u0000bar\": \"qwerty\\u0000asdf\" }";
const char *expected_key = "foo\0bar";
const int expected_key_len = 7;
const char *expected_key_printable = "foo\\0bar";
const char *expected_value = "qwerty\0asdf";
const int expected_value_len = 12;
const char *expected_value_printable = "qwerty\\0asdf";

struct json_object *parsed = json_tokener_parse(input);

printf("Parsed input: %s\n", input);
printf("Result is ");
if (parsed == NULL)
{
printf("NULL (error!)\n");
return 1; // Error somewhere
}
else if (!json_object_is_type(parsed, json_type_object))
{
printf("not `json_type_object` (error!)\n");
return 1; // Error somewhere
}
else
{
printf("`json_type_object`\n");
}

// Check nothing odd happened in parsing
if (json_object_object_length(parsed) != 1)
{
printf("Should contain only one field (has %d) (error!)",
json_object_object_length(parsed));
return 1;
}
json_bool key_present = json_object_object_get_ex(parsed, expected_key, NULL);
if (key_present)
{
printf("The key \"%s\" should not be present when calling "
"`json_object_object_get_ex` "
"(the real key contains a NUL character). (error!)\n",
expected_key);
return 1;
}

// Check the key is present
struct json_object *string;
key_present = json_object_object_get_ex_len(parsed, expected_key, 7, &string);
if (!key_present)
{
printf("The key \"%s\" should be present when calling "
"`json_object_object_get_ex_len` (error!)\n",
expected_key_printable);
return 1;
}
else if (string == NULL)
{
printf("The key \"%s\" was present when calling "
"`json_object_object_get_ex_len`, but got NULL (error!)\n",
expected_key_printable);
return 1;
}
struct json_object *from_get_len = json_object_object_get_len(parsed, expected_key, 7);
if (string != from_get_len)
{
printf("The value returned from `json_object_object_get_len` should be the "
"same as the one from `json_object_object_get_ex_len` (error!)\n"
"Got %p from `json_object_object_get_ex_len` but %p from "
"`json_object_object_get_len`\n",
string, from_get_len);
return 1;
}
else
{
printf("Key was present and same for "
"`json_object_object_get_ex_len` and `json_object_object_get_len`\n");
}

// Check the value is right
if (!json_object_is_type(string, json_type_string))
{
printf(
"Value is wrong type, expected `json_type_string` (%d) but got %d (error!)\n",
json_type_string, json_object_get_type(string));
return 1;
}
else
{
printf("Value is right type, `json_type_string`\n");
}
const int actual_value_len = json_object_get_string_len(string);
const char *actual_value = json_object_get_string(string);
if (actual_value_len != expected_value_len)
{
printf("Value is wrong length, expected %d but got %d (error!)\n",
expected_value_len, actual_value_len);
return 1;
}
else if (memcmp(expected_value, actual_value, actual_value_len) != 0)
{
printf("Expected \"%s\" but got \"", expected_value_printable);
for (int i = 0; i < actual_value_len; ++i)
{
if (actual_value[i] == '\0')
{
puts("\\0");
}
else
{
putchar(actual_value[i]);
}
}
printf("\"\n");
return 1;
}
else
{
printf("Expected value matches actual\n");
}

json_object_put(parsed);
printf("PASS\n");
return 0;
}

+ 6
- 0
tests/test_null_keys_get.expected View File

@@ -0,0 +1,6 @@
Parsed input: { "foo\u0000bar": "qwerty\u0000asdf" }
Result is `json_type_object`
Key was present and same for `json_object_object_get_ex_len` and `json_object_object_get_len`
Value is right type, `json_type_string`
Expected value matches actual
PASS

+ 1
- 0
tests/test_null_keys_get.test View File

@@ -0,0 +1 @@
test_basic.test

Loading…
Cancel
Save