From efd1109ca083926a2384a1dfaf0b75ccad6df618 Mon Sep 17 00:00:00 2001 From: Armin Novak Date: Tue, 30 Sep 2025 09:33:19 +0200 Subject: [PATCH] [winpr,json] fix a memory leak with jansson * Add a reference count debug option to jansson implementation * Fix a memory leak with add_to_object --- winpr/libwinpr/utils/json/jansson.c | 131 ++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 34 deletions(-) diff --git a/winpr/libwinpr/utils/json/jansson.c b/winpr/libwinpr/utils/json/jansson.c index 501d3fe72..7e1dc9e36 100644 --- a/winpr/libwinpr/utils/json/jansson.c +++ b/winpr/libwinpr/utils/json/jansson.c @@ -26,6 +26,65 @@ #endif #include +#if defined(WITH_DEBUG_JANSSON) +#include "../log.h" +#define TAG WINPR_TAG("jansson") + +#define ccast(json) ccast_((json), __func__) +static const json_t* ccast_(const WINPR_JSON* json, const char* fkt) +{ + const json_t* jansson = (const json_t*)json; + if (!jansson) + WLog_DBG(TAG, "%s: NULL", fkt); + else + { + WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount); + } + return jansson; +} + +#define cast(json) cast_((json), __func__) +static json_t* cast_(WINPR_JSON* json, const char* fkt) +{ + json_t* jansson = (json_t*)json; + if (!jansson) + WLog_DBG(TAG, "%s: NULL", fkt); + else + { + WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount); + } + return jansson; +} + +#define revcast(json) revcast_((json), __func__) +static WINPR_JSON* revcast_(json_t* json, const char* fkt) +{ + json_t* jansson = (json_t*)json; + if (!jansson) + WLog_DBG(TAG, "%s: NULL", fkt); + else + { + WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount); + } + return jansson; +} +#else +static inline const json_t* ccast(const WINPR_JSON* json) +{ + return WINPR_CXX_COMPAT_CAST(const json_t*, json); +} + +static inline json_t* cast(WINPR_JSON* json) +{ + return WINPR_CXX_COMPAT_CAST(json_t*, json); +} + +static inline WINPR_JSON* revcast(json_t* json) +{ + return WINPR_CXX_COMPAT_CAST(WINPR_JSON*, json); +} +#endif + int WINPR_JSON_version(char* buffer, size_t len) { return _snprintf(buffer, len, "jansson %s", jansson_version_str()); @@ -34,51 +93,52 @@ int WINPR_JSON_version(char* buffer, size_t len) WINPR_JSON* WINPR_JSON_Parse(const char* value) { json_error_t error = { 0 }; - return json_loads(value, JSON_DECODE_ANY, &error); + return revcast(json_loads(value, JSON_DECODE_ANY, &error)); } WINPR_JSON* WINPR_JSON_ParseWithLength(const char* value, size_t buffer_length) { json_error_t error = { 0 }; - return json_loadb(value, buffer_length, JSON_DECODE_ANY, &error); + return revcast(json_loadb(value, buffer_length, JSON_DECODE_ANY, &error)); } void WINPR_JSON_Delete(WINPR_JSON* item) { - json_delete((json_t*)item); + json_delete(cast(item)); } WINPR_JSON* WINPR_JSON_GetArrayItem(const WINPR_JSON* array, size_t index) { - return json_array_get(array, index); + return revcast(json_array_get(ccast(array), index)); } size_t WINPR_JSON_GetArraySize(const WINPR_JSON* array) { - return json_array_size(array); + return json_array_size(ccast(array)); } WINPR_JSON* WINPR_JSON_GetObjectItem(const WINPR_JSON* object, const char* string) { - void* it = json_object_iter((json_t*)object); + json_t* json = cast(WINPR_CAST_CONST_PTR_AWAY(object, WINPR_JSON*)); + void* it = json_object_iter(json); while (it) { const char* name = json_object_iter_key(it); if (_stricmp(name, string) == 0) - return json_object_iter_value(it); - it = json_object_iter_next((json_t*)object, it); + return revcast(json_object_iter_value(it)); + it = json_object_iter_next(json, it); } return NULL; } WINPR_JSON* WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON* object, const char* string) { - return json_object_get(object, string); + return revcast(json_object_get(ccast(object), string)); } BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON* object, const char* string) { - return json_object_get(object, string) != NULL; + return json_object_get(ccast(object), string) != NULL; } const char* WINPR_JSON_GetErrorPtr(void) @@ -88,16 +148,17 @@ const char* WINPR_JSON_GetErrorPtr(void) const char* WINPR_JSON_GetStringValue(WINPR_JSON* item) { - return json_string_value(item); + return json_string_value(cast(item)); } double WINPR_JSON_GetNumberValue(const WINPR_JSON* item) { - return json_real_value((const json_t*)item); + return json_real_value(ccast(item)); } -BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item) +BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* json) { + const json_t* item = ccast(json); if (WINPR_JSON_IsArray(item)) return FALSE; if (WINPR_JSON_IsObject(item)) @@ -115,91 +176,93 @@ BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item) BOOL WINPR_JSON_IsFalse(const WINPR_JSON* item) { - return json_is_false((const json_t*)item); + return json_is_false(ccast(item)); } BOOL WINPR_JSON_IsTrue(const WINPR_JSON* item) { - return json_is_true((const json_t*)item); + return json_is_true(ccast(item)); } BOOL WINPR_JSON_IsBool(const WINPR_JSON* item) { - return json_is_boolean((const json_t*)item); + return json_is_boolean(ccast(item)); } BOOL WINPR_JSON_IsNull(const WINPR_JSON* item) { - return json_is_null((const json_t*)item); + return json_is_null(ccast(item)); } BOOL WINPR_JSON_IsNumber(const WINPR_JSON* item) { - return json_is_number((const json_t*)item); + return json_is_number(ccast(item)); } BOOL WINPR_JSON_IsString(const WINPR_JSON* item) { - return json_is_string((const json_t*)item); + return json_is_string(ccast(item)); } BOOL WINPR_JSON_IsArray(const WINPR_JSON* item) { - return json_is_array((const json_t*)item); + return json_is_array(ccast(item)); } BOOL WINPR_JSON_IsObject(const WINPR_JSON* item) { - return json_is_array((const json_t*)item); + return json_is_array(ccast(item)); } WINPR_JSON* WINPR_JSON_CreateNull(void) { - return json_null(); + return revcast(json_null()); } WINPR_JSON* WINPR_JSON_CreateTrue(void) { - return json_true(); + return revcast(json_true()); } WINPR_JSON* WINPR_JSON_CreateFalse(void) { - return json_false(); + return revcast(json_false()); } WINPR_JSON* WINPR_JSON_CreateBool(BOOL boolean) { - return json_boolean(boolean); + return revcast(json_boolean(boolean)); } WINPR_JSON* WINPR_JSON_CreateNumber(double num) { - return json_real(num); + return revcast(json_real(num)); } WINPR_JSON* WINPR_JSON_CreateString(const char* string) { - return json_string(string); + return revcast(json_string(string)); } WINPR_JSON* WINPR_JSON_CreateArray(void) { - return json_array(); + return revcast(json_array()); } WINPR_JSON* WINPR_JSON_CreateObject(void) { - return json_object(); + return revcast(json_object()); } static WINPR_JSON* add_to_object(WINPR_JSON* object, const char* name, json_t* obj) { if (!obj) return NULL; - if (json_object_set_new(object, name, obj) != 0) + const BOOL rc = (json_object_set_new(cast(object), name, obj) != 0); + json_decref(obj); + if (!rc) return NULL; - return obj; + return revcast(obj); } WINPR_JSON* WINPR_JSON_AddNullToObject(WINPR_JSON* object, const char* name) @@ -246,7 +309,7 @@ WINPR_JSON* WINPR_JSON_AddObjectToObject(WINPR_JSON* object, const char* name) BOOL WINPR_JSON_AddItemToArray(WINPR_JSON* array, WINPR_JSON* item) { - return json_array_append(array, item) == 0; + return json_array_append(cast(array), item) == 0; } WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name) @@ -257,10 +320,10 @@ WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name) char* WINPR_JSON_Print(WINPR_JSON* item) { - return json_dumps(item, JSON_INDENT(2) | JSON_ENSURE_ASCII | JSON_SORT_KEYS); + return json_dumps(cast(item), JSON_INDENT(2) | JSON_ENSURE_ASCII | JSON_SORT_KEYS); } char* WINPR_JSON_PrintUnformatted(WINPR_JSON* item) { - return json_dumps(item, JSON_COMPACT | JSON_ENSURE_ASCII | JSON_SORT_KEYS); + return json_dumps(cast(item), JSON_COMPACT | JSON_ENSURE_ASCII | JSON_SORT_KEYS); }