json_object: fix double format use-after-free on strdup failure

json_c_set_serialization_double_format() freed the current format string
before strdup() of its replacement. In the GLOBAL branch the freed
pointer was not reset on the strdup() failure path, leaving it dangling:
a later serialization read through the dangling pointer, and a
subsequent set(NULL) call freed it a second time. The THREAD branch
reset its pointer to NULL right after the free, so it had no dangling
pointer; its only defect was that a strdup() failure silently dropped
the configured thread-local format and fell back to the global format.

strdup() the new format first and replace the old one only once it
succeeds, so a failure leaves both the global and thread-local format
exactly as they were before the call.

Fixes #997.
This commit is contained in:
zhouyunding authored and Eric Hawicz committed 2026-11-08 23:34:55 -05:00
1 parent 7f4196213a
commit 0f8fe5dd53
1 file changed
+19 -22
+19 -22
View File
@@ -1195,6 +1195,23 @@ int json_c_set_serialization_double_format(const char *double_format, int global
{
if (global_or_thread == JSON_C_OPTION_GLOBAL)
{
if (double_format)
{
char *p = strdup(double_format);
if (p == NULL)
{
_json_c_set_last_err("json_c_set_serialization_double_format: "
"out of memory\n");
return -1;
}
free(global_serialization_float_format);
global_serialization_float_format = p;
}
else
{
free(global_serialization_float_format);
global_serialization_float_format = NULL;
}
#if defined(HAVE___THREAD)
if (tls_serialization_float_format)
{
@@ -1202,32 +1219,10 @@ int json_c_set_serialization_double_format(const char *double_format, int global
tls_serialization_float_format = NULL;
}
#endif
if (global_serialization_float_format)
free(global_serialization_float_format);
if (double_format)
{
char *p = strdup(double_format);
if (p == NULL)
{
_json_c_set_last_err("json_c_set_serialization_double_format: "
"out of memory\n");
return -1;
}
global_serialization_float_format = p;
}
else
{
global_serialization_float_format = NULL;
}
}
else if (global_or_thread == JSON_C_OPTION_THREAD)
{
#if defined(HAVE___THREAD)
if (tls_serialization_float_format)
{
free(tls_serialization_float_format);
tls_serialization_float_format = NULL;
}
if (double_format)
{
char *p = strdup(double_format);
@@ -1237,10 +1232,12 @@ int json_c_set_serialization_double_format(const char *double_format, int global
"out of memory\n");
return -1;
}
free(tls_serialization_float_format);
tls_serialization_float_format = p;
}
else
{
free(tls_serialization_float_format);
tls_serialization_float_format = NULL;
}
#else