From d2934f97683e857efb1262d65b4fd0e58642a28e Mon Sep 17 00:00:00 2001 From: Shveta Malik Date: Tue, 1 Sep 2026 14:38:51 +0530 Subject: [PATCH] reentrant json fix --- src/backend/utils/adt/json.c | 96 ++++++++++++++++++++++++++++-------- 1 file changed, 75 insertions(+), 21 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index babbaed6392..aa41665a2e6 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -348,34 +348,72 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result, } break; case JSONTYPE_JSON: - - /* - * JSON and JSONB output are already escaped, so we can call their - * output functions directly without extra escaping. Check the - * rendered length before appending to result. - */ - outputstr = OidOutputFunctionCall(outfuncoid, val); - if (json_size_would_exceed(result->len, strlen(outputstr))) { + Size saved_limit = json_size_limit; + bool saved_hit = json_size_limit_hit; + + /* + * Suspend the size limit before invoking type output or cast + * functions. This prevents intermediate internal operations + * from prematurely triggering the limit, while the final + * returned output is still strictly measured against the + * threshold immediately after. + */ + json_set_size_limit(0); + + /* + * JSON and JSONB output are already escaped, so we can call + * their output functions directly without extra escaping. + * Check the rendered length before appending to result. + */ + outputstr = OidOutputFunctionCall(outfuncoid, val); + + json_size_limit = saved_limit; + json_size_limit_hit = saved_hit; + + if (json_size_would_exceed(result->len, strlen(outputstr))) + { + pfree(outputstr); + return; + } + + appendStringInfoString(result, outputstr); pfree(outputstr); - return; + break; } - - appendStringInfoString(result, outputstr); - pfree(outputstr); - break; case JSONTYPE_CAST: - /* outfuncoid refers to a cast function, not an output function */ - jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val)); - if (json_size_would_exceed(result->len, VARSIZE_ANY_EXHDR(jsontext))) { + Size saved_limit = json_size_limit; + bool saved_hit = json_size_limit_hit; + + /* + * Suspend the size limit before invoking type output or cast + * functions. This prevents intermediate internal operations + * from prematurely triggering the limit, while the final + * returned output is still strictly measured against the + * threshold immediately after. + */ + json_set_size_limit(0); + + /* + * outfuncoid refers to a cast function, not an output + * function + */ + jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val)); + + json_size_limit = saved_limit; + json_size_limit_hit = saved_hit; + + if (json_size_would_exceed(result->len, VARSIZE_ANY_EXHDR(jsontext))) + { + pfree(jsontext); + return; + } + appendBinaryStringInfo(result, VARDATA_ANY(jsontext), + VARSIZE_ANY_EXHDR(jsontext)); pfree(jsontext); - return; + break; } - appendBinaryStringInfo(result, VARDATA_ANY(jsontext), - VARSIZE_ANY_EXHDR(jsontext)); - pfree(jsontext); - break; default: /* special-case text types to save useless palloc/memcpy cycles */ if (outfuncoid == F_TEXTOUT || outfuncoid == F_VARCHAROUT || @@ -397,7 +435,23 @@ datum_to_json_internal(Datum val, bool is_null, StringInfo result, } else { + Size saved_limit = json_size_limit; + bool saved_hit = json_size_limit_hit; + + /* + * Suspend the size limit before invoking type output or cast + * functions. This prevents intermediate internal operations + * from prematurely triggering the limit, while the final + * returned output is still strictly measured against the + * threshold immediately after. + */ + json_set_size_limit(0); + outputstr = OidOutputFunctionCall(outfuncoid, val); + + json_size_limit = saved_limit; + json_size_limit_hit = saved_hit; + if (json_size_would_exceed(result->len, 6 * strlen(outputstr))) { pfree(outputstr); -- 2.34.1