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:
Petri Lehtinen 2011-03-30 12:57:24 +03:00
parent 279d8bf108
commit 0944ac8d91
4 changed files with 34 additions and 16 deletions

View File

@ -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;

View File

@ -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;
} }

View File

@ -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);

View File

@ -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);