Fix invalid object key hashing in json_unpack() strict checking mode
The keys which are stored temporarily to a hashtable to make sure that all object keys are unpacked, were hashed with the object key hashing function. It is meant to compute hashes for object_key_t values, and it works incorrectly for plain strings. Fixed by introducing suitable functions for hashing and comparing strings for string-keyed hashtables.
This commit is contained in:
parent
279d8bf108
commit
0944ac8d91
@ -66,8 +66,8 @@ typedef struct {
|
|||||||
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
#define json_to_real(json_) container_of(json_, json_real_t, json)
|
||||||
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
#define json_to_integer(json_) container_of(json_, json_integer_t, json)
|
||||||
|
|
||||||
size_t jsonp_hash_key(const void *ptr);
|
size_t jsonp_hash_str(const void *ptr);
|
||||||
int jsonp_key_equal(const void *ptr1, const void *ptr2);
|
int jsonp_str_equal(const void *ptr1, const void *ptr2);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t serial;
|
size_t serial;
|
||||||
|
@ -233,7 +233,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
|
|||||||
*/
|
*/
|
||||||
hashtable_t key_set;
|
hashtable_t key_set;
|
||||||
|
|
||||||
if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
|
if(hashtable_init(&key_set, jsonp_hash_str, jsonp_str_equal, NULL, NULL)) {
|
||||||
set_error(s, "<internal>", "Out of memory");
|
set_error(s, "<internal>", "Out of memory");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
35
src/value.c
35
src/value.c
@ -26,15 +26,10 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
|
|||||||
|
|
||||||
/*** object ***/
|
/*** object ***/
|
||||||
|
|
||||||
/* This macro just returns a pointer that's a few bytes backwards from
|
/* From http://www.cse.yorku.ca/~oz/hash.html */
|
||||||
string. This makes it possible to pass a pointer to object_key_t
|
size_t jsonp_hash_str(const void *ptr)
|
||||||
when only the string inside it is used, without actually creating
|
|
||||||
an object_key_t instance. */
|
|
||||||
#define string_to_key(string) container_of(string, object_key_t, key)
|
|
||||||
|
|
||||||
size_t jsonp_hash_key(const void *ptr)
|
|
||||||
{
|
{
|
||||||
const char *str = ((const object_key_t *)ptr)->key;
|
const char *str = (const char *)ptr;
|
||||||
|
|
||||||
size_t hash = 5381;
|
size_t hash = 5381;
|
||||||
size_t c;
|
size_t c;
|
||||||
@ -48,10 +43,26 @@ size_t jsonp_hash_key(const void *ptr)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
int jsonp_key_equal(const void *ptr1, const void *ptr2)
|
int jsonp_str_equal(const void *ptr1, const void *ptr2)
|
||||||
{
|
{
|
||||||
return strcmp(((const object_key_t *)ptr1)->key,
|
return strcmp((const char *)ptr1, (const char *)ptr2) == 0;
|
||||||
((const object_key_t *)ptr2)->key) == 0;
|
}
|
||||||
|
|
||||||
|
/* This macro just returns a pointer that's a few bytes backwards from
|
||||||
|
string. This makes it possible to pass a pointer to object_key_t
|
||||||
|
when only the string inside it is used, without actually creating
|
||||||
|
an object_key_t instance. */
|
||||||
|
#define string_to_key(string) container_of(string, object_key_t, key)
|
||||||
|
|
||||||
|
static size_t hash_key(const void *ptr)
|
||||||
|
{
|
||||||
|
return jsonp_hash_str(((const object_key_t *)ptr)->key);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int key_equal(const void *ptr1, const void *ptr2)
|
||||||
|
{
|
||||||
|
return jsonp_str_equal(((const object_key_t *)ptr1)->key,
|
||||||
|
((const object_key_t *)ptr2)->key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void value_decref(void *value)
|
static void value_decref(void *value)
|
||||||
@ -67,7 +78,7 @@ json_t *json_object(void)
|
|||||||
json_init(&object->json, JSON_OBJECT);
|
json_init(&object->json, JSON_OBJECT);
|
||||||
|
|
||||||
if(hashtable_init(&object->hashtable,
|
if(hashtable_init(&object->hashtable,
|
||||||
jsonp_hash_key, jsonp_key_equal,
|
hash_key, key_equal,
|
||||||
jsonp_free, value_decref))
|
jsonp_free, value_decref))
|
||||||
{
|
{
|
||||||
jsonp_free(object);
|
jsonp_free(object);
|
||||||
|
@ -122,6 +122,13 @@ int main()
|
|||||||
fail("json_unpack simple array failed");
|
fail("json_unpack simple array failed");
|
||||||
json_decref(j);
|
json_decref(j);
|
||||||
|
|
||||||
|
/* object with many items & strict checking */
|
||||||
|
j = json_pack("{s:i, s:i, s:i}", "a", 1, "b", 2, "c", 3);
|
||||||
|
rv = json_unpack(j, "{s:i, s:i, s:i}", "a", &i1, "b", &i2, "c", &i3);
|
||||||
|
if(rv || i1 != 1 || i2 != 2 || i3 != 3)
|
||||||
|
fail("json_unpack object with many items failed");
|
||||||
|
json_decref(j);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalid cases
|
* Invalid cases
|
||||||
*/
|
*/
|
||||||
@ -285,7 +292,7 @@ int main()
|
|||||||
/* Unpack the same item twice */
|
/* Unpack the same item twice */
|
||||||
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
j = json_pack("{s:s, s:i}", "foo", "bar", "baz", 42);
|
||||||
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
if(!json_unpack_ex(j, &error, 0, "{s:s,s:s!}", "foo", &s, "foo", &s))
|
||||||
fail("json_unpack object with strict validation failed");
|
fail("json_unpack object with strict validation failed");
|
||||||
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
check_error("1 object item(s) left unpacked", "<validation>", 1, 10, 10);
|
||||||
json_decref(j);
|
json_decref(j);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user