Fix invalid object key hashing in json_unpack() strict checking mode
authorPetri Lehtinen <petri@digip.org>
Wed, 30 Mar 2011 09:57:24 +0000 (12:57 +0300)
committerPetri Lehtinen <petri@digip.org>
Wed, 30 Mar 2011 09:57:48 +0000 (12:57 +0300)
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.

src/jansson_private.h
src/pack_unpack.c
src/value.c
test/suites/api/test_unpack.c

index 9224b31..452a4f0 100644 (file)
@@ -66,8 +66,8 @@ typedef struct {
 #define json_to_real(json_)   container_of(json_, json_real_t, json)
 #define json_to_integer(json_) container_of(json_, json_integer_t, json)
 
-size_t jsonp_hash_key(const void *ptr);
-int jsonp_key_equal(const void *ptr1, const void *ptr2);
+size_t jsonp_hash_str(const void *ptr);
+int jsonp_str_equal(const void *ptr1, const void *ptr2);
 
 typedef struct {
     size_t serial;
index 5001764..20d540b 100644 (file)
@@ -233,7 +233,7 @@ static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
     */
     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");
         return -1;
     }
index 937aa95..5d8ae22 100644 (file)
@@ -26,15 +26,10 @@ static JSON_INLINE void json_init(json_t *json, json_type type)
 
 /*** object ***/
 
-/* 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)
-
-size_t jsonp_hash_key(const void *ptr)
+/* From http://www.cse.yorku.ca/~oz/hash.html */
+size_t jsonp_hash_str(const void *ptr)
 {
-    const char *str = ((const object_key_t *)ptr)->key;
+    const char *str = (const char *)ptr;
 
     size_t hash = 5381;
     size_t c;
@@ -48,10 +43,26 @@ size_t jsonp_hash_key(const void *ptr)
     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 char *)ptr1, (const char *)ptr2) == 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 strcmp(((const object_key_t *)ptr1)->key,
-                  ((const object_key_t *)ptr2)->key) == 0;
+    return jsonp_str_equal(((const object_key_t *)ptr1)->key,
+                           ((const object_key_t *)ptr2)->key);
 }
 
 static void value_decref(void *value)
@@ -67,7 +78,7 @@ json_t *json_object(void)
     json_init(&object->json, JSON_OBJECT);
 
     if(hashtable_init(&object->hashtable,
-                      jsonp_hash_key, jsonp_key_equal,
+                      hash_key, key_equal,
                       jsonp_free, value_decref))
     {
         jsonp_free(object);
index cfc68ea..9426104 100644 (file)
@@ -122,6 +122,13 @@ int main()
         fail("json_unpack simple array failed");
     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
      */
@@ -285,7 +292,7 @@ int main()
     /* Unpack the same item twice */
     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))
-       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);
     json_decref(j);