json_dump: Fix thread safety issue.

Circular reference detection in json_dump was not thread safe.  Replace
visited flag with a hashtable_t.

Issue #387
This commit is contained in:
Corey Farrell 2018-01-22 15:39:58 -05:00
parent dc3b313e91
commit 37e0ee4d48
3 changed files with 48 additions and 48 deletions

View File

@ -196,8 +196,17 @@ static int compare_keys(const void *key1, const void *key2)
return strcmp(*(const char **)key1, *(const char **)key2); return strcmp(*(const char **)key1, *(const char **)key2);
} }
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
{
snprintf(key, key_size, "%p", json);
if (hashtable_get(parents, key))
return -1;
return hashtable_set(parents, key, json_null());
}
static int do_dump(const json_t *json, size_t flags, int depth, static int do_dump(const json_t *json, size_t flags, int depth,
json_dump_callback_t dump, void *data) hashtable_t *parents, json_dump_callback_t dump, void *data)
{ {
int embed = flags & JSON_EMBED; int embed = flags & JSON_EMBED;
@ -251,58 +260,53 @@ static int do_dump(const json_t *json, size_t flags, int depth,
{ {
size_t n; size_t n;
size_t i; size_t i;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
json_array_t *array; char key[2 + (sizeof(json) * 2) + 1];
/* detect circular references */ /* detect circular references */
array = json_to_array(json); if (loop_check(parents, json, key, sizeof(key)))
if(array->visited) return -1;
goto array_error;
array->visited = 1;
n = json_array_size(json); n = json_array_size(json);
if(!embed && dump("[", 1, data)) if(!embed && dump("[", 1, data))
goto array_error; return -1;
if(n == 0) { if(n == 0) {
array->visited = 0; hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data); return embed ? 0 : dump("]", 1, data);
} }
if(dump_indent(flags, depth + 1, 0, dump, data)) if(dump_indent(flags, depth + 1, 0, dump, data))
goto array_error; return -1;
for(i = 0; i < n; ++i) { for(i = 0; i < n; ++i) {
if(do_dump(json_array_get(json, i), flags, depth + 1, if(do_dump(json_array_get(json, i), flags, depth + 1,
dump, data)) parents, dump, data))
goto array_error; return -1;
if(i < n - 1) if(i < n - 1)
{ {
if(dump(",", 1, data) || if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data)) dump_indent(flags, depth + 1, 1, dump, data))
goto array_error; return -1;
} }
else else
{ {
if(dump_indent(flags, depth, 0, dump, data)) if(dump_indent(flags, depth, 0, dump, data))
goto array_error; return -1;
} }
} }
array->visited = 0; hashtable_del(parents, key);
return embed ? 0 : dump("]", 1, data); return embed ? 0 : dump("]", 1, data);
array_error:
array->visited = 0;
return -1;
} }
case JSON_OBJECT: case JSON_OBJECT:
{ {
json_object_t *object;
void *iter; void *iter;
const char *separator; const char *separator;
int separator_length; int separator_length;
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
char key[2 + (sizeof(json) * 2) + 1];
if(flags & JSON_COMPACT) { if(flags & JSON_COMPACT) {
separator = ":"; separator = ":";
@ -314,21 +318,19 @@ static int do_dump(const json_t *json, size_t flags, int depth,
} }
/* detect circular references */ /* detect circular references */
object = json_to_object(json); if (loop_check(parents, json, key, sizeof(key)))
if(object->visited) return -1;
goto object_error;
object->visited = 1;
iter = json_object_iter((json_t *)json); iter = json_object_iter((json_t *)json);
if(!embed && dump("{", 1, data)) if(!embed && dump("{", 1, data))
goto object_error; return -1;
if(!iter) { if(!iter) {
object->visited = 0; hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data); return embed ? 0 : dump("}", 1, data);
} }
if(dump_indent(flags, depth + 1, 0, dump, data)) if(dump_indent(flags, depth + 1, 0, dump, data))
goto object_error; return -1;
if(flags & JSON_SORT_KEYS) if(flags & JSON_SORT_KEYS)
{ {
@ -338,7 +340,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
size = json_object_size(json); size = json_object_size(json);
keys = jsonp_malloc(size * sizeof(const char *)); keys = jsonp_malloc(size * sizeof(const char *));
if(!keys) if(!keys)
goto object_error; return -1;
i = 0; i = 0;
while(iter) while(iter)
@ -362,10 +364,10 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_string(key, strlen(key), dump, data, flags); dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) || if(dump(separator, separator_length, data) ||
do_dump(value, flags, depth + 1, dump, data)) do_dump(value, flags, depth + 1, parents, dump, data))
{ {
jsonp_free(keys); jsonp_free(keys);
goto object_error; return -1;
} }
if(i < size - 1) if(i < size - 1)
@ -374,7 +376,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_indent(flags, depth + 1, 1, dump, data)) dump_indent(flags, depth + 1, 1, dump, data))
{ {
jsonp_free(keys); jsonp_free(keys);
goto object_error; return -1;
} }
} }
else else
@ -382,7 +384,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
if(dump_indent(flags, depth, 0, dump, data)) if(dump_indent(flags, depth, 0, dump, data))
{ {
jsonp_free(keys); jsonp_free(keys);
goto object_error; return -1;
} }
} }
} }
@ -401,31 +403,27 @@ static int do_dump(const json_t *json, size_t flags, int depth,
dump_string(key, strlen(key), dump, data, flags); dump_string(key, strlen(key), dump, data, flags);
if(dump(separator, separator_length, data) || if(dump(separator, separator_length, data) ||
do_dump(json_object_iter_value(iter), flags, depth + 1, do_dump(json_object_iter_value(iter), flags, depth + 1,
dump, data)) parents, dump, data))
goto object_error; return -1;
if(next) if(next)
{ {
if(dump(",", 1, data) || if(dump(",", 1, data) ||
dump_indent(flags, depth + 1, 1, dump, data)) dump_indent(flags, depth + 1, 1, dump, data))
goto object_error; return -1;
} }
else else
{ {
if(dump_indent(flags, depth, 0, dump, data)) if(dump_indent(flags, depth, 0, dump, data))
goto object_error; return -1;
} }
iter = next; iter = next;
} }
} }
object->visited = 0; hashtable_del(parents, key);
return embed ? 0 : dump("}", 1, data); return embed ? 0 : dump("}", 1, data);
object_error:
object->visited = 0;
return -1;
} }
default: default:
@ -489,10 +487,18 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags) int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
{ {
int res;
hashtable_t parents_set;
if(!(flags & JSON_ENCODE_ANY)) { if(!(flags & JSON_ENCODE_ANY)) {
if(!json_is_array(json) && !json_is_object(json)) if(!json_is_array(json) && !json_is_object(json))
return -1; return -1;
} }
return do_dump(json, flags, 0, callback, data); if (hashtable_init(&parents_set))
return -1;
res = do_dump(json, flags, 0, &parents_set, callback, data);
hashtable_close(&parents_set);
return res;
} }

View File

@ -35,7 +35,6 @@
typedef struct { typedef struct {
json_t json; json_t json;
hashtable_t hashtable; hashtable_t hashtable;
int visited;
} json_object_t; } json_object_t;
typedef struct { typedef struct {
@ -43,7 +42,6 @@ typedef struct {
size_t size; size_t size;
size_t entries; size_t entries;
json_t **table; json_t **table;
int visited;
} json_array_t; } json_array_t;
typedef struct { typedef struct {

View File

@ -67,8 +67,6 @@ json_t *json_object(void)
return NULL; return NULL;
} }
object->visited = 0;
return &object->json; return &object->json;
} }
@ -354,8 +352,6 @@ json_t *json_array(void)
return NULL; return NULL;
} }
array->visited = 0;
return &array->json; return &array->json;
} }