Hi Tom, Thanks for all your great feedback.
> No, it'd be O(N^2) for an N-deep reference chain. Admittedly, > realistic use-cases would never have more than a couple of layers of > indirection. But this whole exercise is to guard against adversarial > inputs, I think. I don't really want to add cycles and complexity to > make our behavior a bit more friendly in cases that nobody is going > to get into unless they are trying to break the database. OK, that's fair enough. Here is the corrected patch. ``` =# SELECT perl_circular(); ^CCancel request sent ERROR: canceling statement due to user request CONTEXT: PL/Perl function "perl_circular" ``` -- Best regards, Aleksander Alekseev
From eb4209a45852d522166f2ec3299b903850695049 Mon Sep 17 00:00:00 2001 From: Aleksander Alekseev <[email protected]> Date: Wed, 17 Jun 2026 13:59:50 +0300 Subject: [PATCH v2] jsonb_plperl, jsonb_plpython: Fix unguarded recursion and loops Add check_stack_depth() to Jsonb_to_SV, SV_to_JsonbValue, PLyObject_FromJsonbContainer, and PLyObject_ToJsonbValue. Without this, deeply nested JSONB values crash the backend with SIGSEGV instead of raising a proper error. Also add CHECK_FOR_INTERRUPTS() to the while loop in SV_to_JsonbValue that dereferences chains of Perl references, so that a circular reference (e.g. $x = \$x) can be cancelled by the user instead of spinning indefinitely. Author: Aleksander Alekseev <[email protected]> Reviewed-by: Tom Lane <[email protected]> Discussion: https://postgr.es/m/CAJ7c6TPbjkzUk4qJ5dHvDNEz0hBuFue3A-XWz_%3D897z%2BBC%2Bz8A%40mail.gmail.com --- contrib/jsonb_plperl/jsonb_plperl.c | 8 ++++++++ contrib/jsonb_plpython/jsonb_plpython.c | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/contrib/jsonb_plperl/jsonb_plperl.c b/contrib/jsonb_plperl/jsonb_plperl.c index f8e4a584fdd..c0d3b521141 100644 --- a/contrib/jsonb_plperl/jsonb_plperl.c +++ b/contrib/jsonb_plperl/jsonb_plperl.c @@ -3,6 +3,7 @@ #include <math.h> #include "fmgr.h" +#include "miscadmin.h" #include "plperl.h" #include "utils/fmgrprotos.h" #include "utils/jsonb.h" @@ -66,6 +67,8 @@ Jsonb_to_SV(JsonbContainer *jsonb) JsonbIterator *it; JsonbIteratorToken r; + check_stack_depth(); + it = JsonbIteratorInit(jsonb); r = JsonbIteratorNext(&it, &v, true); @@ -179,9 +182,14 @@ SV_to_JsonbValue(SV *in, JsonbInState *jsonb_state, bool is_elem) dTHX; JsonbValue out; /* result */ + check_stack_depth(); + /* Dereference references recursively. */ while (SvROK(in)) + { + CHECK_FOR_INTERRUPTS(); in = SvRV(in); + } switch (SvTYPE(in)) { diff --git a/contrib/jsonb_plpython/jsonb_plpython.c b/contrib/jsonb_plpython/jsonb_plpython.c index 4de75a04e76..f6eddb81c48 100644 --- a/contrib/jsonb_plpython/jsonb_plpython.c +++ b/contrib/jsonb_plpython/jsonb_plpython.c @@ -1,5 +1,6 @@ #include "postgres.h" +#include "miscadmin.h" #include "plpy_elog.h" #include "plpy_typeio.h" #include "plpy_util.h" @@ -143,6 +144,8 @@ PLyObject_FromJsonbContainer(JsonbContainer *jsonb) JsonbIterator *it; PyObject *result; + check_stack_depth(); + it = JsonbIteratorInit(jsonb); r = JsonbIteratorNext(&it, &v, true); @@ -410,6 +413,8 @@ PLyObject_ToJsonbValue(PyObject *obj, JsonbInState *jsonb_state, bool is_elem) { JsonbValue *out; + check_stack_depth(); + if (!PyUnicode_Check(obj)) { if (PySequence_Check(obj)) -- 2.43.0
