]> git.ipfire.org Git - thirdparty/json-c.git/commitdiff
Issue #934: Be sure to clear _user_delete in _json_object_put_maybe_free() to avoid...
authorEric Hawicz <erh+git@nimenees.com>
Sat, 4 Jul 2026 15:02:06 +0000 (11:02 -0400)
committerEric Hawicz <erh+git@nimenees.com>
Sat, 4 Jul 2026 15:02:06 +0000 (11:02 -0400)
json_object.c
json_object.h
tests/test_deep_nesting.c

index 0a619679bd7ea9f0499bdbe1cfc0e55fdd3c1301..6cb1bdac86d5ec7dc6b4f6d8b8ccd687393e47e3 100644 (file)
@@ -303,6 +303,7 @@ static inline int _json_object_put_maybe_free(struct json_object *jso, int free_
 
        if (jso->_user_delete)
                jso->_user_delete(jso, jso->_userdata);
+       jso->_user_delete = NULL;
 
        switch (jso->o_type)
        {
index e21138ecf1db7fc5eb0d5befca97aa3217b06d71..7edb63d4f8cbeb75e8d16eaea1726feaec9f4ca7 100644 (file)
@@ -259,7 +259,7 @@ JSON_EXPORT void *json_object_get_userdata(json_object *jso);
  * The user_delete parameter is optional and may be passed as NULL, even if
  * the userdata parameter is non-NULL.  It will be called just before the
  * json_object is deleted, after it's reference count goes to zero
- * (see json_object_put()).
+ * (see json_object_put()) but before any child objects are freed.
  * If this is not provided, it is up to the caller to free the userdata at
  * an appropriate time. (i.e. after the json_object is deleted)
  *
@@ -293,7 +293,7 @@ JSON_EXPORT void json_object_set_userdata(json_object *jso, void *userdata,
  * The user_delete parameter is optional and may be passed as NULL, even if
  * the userdata parameter is non-NULL.  It will be called just before the
  * json_object is deleted, after it's reference count goes to zero
- * (see json_object_put()).
+ * (see json_object_put()) but before any child objects are freed.
  * If this is not provided, it is up to the caller to free the userdata at
  * an appropriate time. (i.e. after the json_object is deleted)
  *
index 364c4820ea5dfa42e66ea3ccfee7c9eec5da8c5f..9e251750c480bc0d5caaaca0faffee4f3145ccce 100644 (file)
@@ -56,6 +56,37 @@ static void test_deep_nesting_tostring(const char *str)
        json_tokener_free(tok);
 }
 
+struct userdata_test {
+       int userdata_val;
+       char *p;
+};
+/*
+ * Check that the user_delete function is only called once, even with the
+ * newer code to avoid deeply nested calls during json_object_put().
+ */
+static void user_delete_test(struct json_object *jso, void *userdata_in)
+{
+       struct userdata_test *userdata = (struct userdata_test *)userdata_in;
+       printf("in user_delete, userdata_val=%d\n", userdata->userdata_val);
+       fflush(stdout);
+       userdata->userdata_val = 0;
+       userdata->p[0] = 'x';
+       userdata->p[8191] = 'x';
+       free(userdata->p);
+}
+static void test_nesting_with_user_delete(void)
+{
+       json_object *jso;
+       struct userdata_test userdata_val = {
+               1, malloc(8192)
+       };
+
+       jso = json_object_new_object();
+       json_object_set_userdata(jso, &userdata_val, user_delete_test);
+       json_object_object_add(jso, "somekey", json_object_new_string("foo"));
+       json_object_put(jso);
+}
+
 int main(int argc, char **argv)
 {
        char *str;      
@@ -77,5 +108,7 @@ int main(int argc, char **argv)
 
        free(str);
 
+       test_nesting_with_user_delete();
+
        return EXIT_SUCCESS;
 }