agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v2 4/7] Row pattern recognition patch (executor).
271+ messages / 3 participants
[nested] [flat]

* [PATCH v2 4/7] Row pattern recognition patch (executor).
@ 2023-06-26 08:05 Tatsuo Ishii <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Tatsuo Ishii @ 2023-06-26 08:05 UTC (permalink / raw)

---
 src/backend/executor/nodeWindowAgg.c | 225 +++++++++++++++++++-
 src/backend/utils/adt/windowfuncs.c  | 302 ++++++++++++++++++++++++++-
 src/include/catalog/pg_proc.dat      |   9 +
 src/include/nodes/execnodes.h        |  13 ++
 src/include/windowapi.h              |   9 +
 5 files changed, 548 insertions(+), 10 deletions(-)

diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 310ac23e3a..bef2bc62b2 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -48,6 +48,7 @@
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
+#include "utils/fmgroids.h"
 #include "utils/expandeddatum.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -159,6 +160,14 @@ typedef struct WindowStatePerAggData
 	bool		restart;		/* need to restart this agg in this cycle? */
 } WindowStatePerAggData;
 
+/*
+ * Map between Var attno in a target list and the parsed attno.
+ */
+typedef struct AttnoMap {
+	List		*attno;			/* att number in target list (list of AttNumber) */
+	List		*attnosyn;		/* parsed att number (list of AttNumber) */
+} AttnoMap;
+
 static void initialize_windowaggregate(WindowAggState *winstate,
 									   WindowStatePerFunc perfuncstate,
 									   WindowStatePerAgg peraggstate);
@@ -195,9 +204,9 @@ static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
 
 static bool are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
 					  TupleTableSlot *slot2);
-static bool window_gettupleslot(WindowObject winobj, int64 pos,
-								TupleTableSlot *slot);
 
+static void attno_map(Node *node, AttnoMap *map);
+static bool attno_map_walker(Node *node, void *context);
 
 /*
  * initialize_windowaggregate
@@ -2388,6 +2397,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	TupleDesc	scanDesc;
 	ListCell   *l;
 
+	TargetEntry	*te;
+	Expr		*expr;
+	Var			*var;
+	int			nargs;
+	AttnoMap	attnomap;
+
 	/* check for unsupported flags */
 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
 
@@ -2483,6 +2498,16 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate, scanDesc,
 												   &TTSOpsMinimalTuple);
 
+	winstate->prev_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+												 &TTSOpsMinimalTuple);
+
+	winstate->next_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+												 &TTSOpsMinimalTuple);
+
+	winstate->null_slot = ExecInitExtraTupleSlot(estate, scanDesc,
+												 &TTSOpsMinimalTuple);
+	winstate->null_slot = ExecStoreAllNullTuple(winstate->null_slot);
+
 	/*
 	 * create frame head and tail slots only if needed (must create slots in
 	 * exactly the same cases that update_frameheadpos and update_frametailpos
@@ -2667,6 +2692,71 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	winstate->inRangeAsc = node->inRangeAsc;
 	winstate->inRangeNullsFirst = node->inRangeNullsFirst;
 
+	/* Set up SKIP TO type */
+	winstate->rpSkipTo = node->rpSkipTo;
+	/* Set up row pattern recognition PATTERN clause */
+	winstate->patternVariableList = node->patternVariable;
+	winstate->patternRegexpList = node->patternRegexp;
+
+	/* Set up row pattern recognition DEFINE clause */
+
+	/*
+	 * Collect mapping between varattno and varattnosyn in the targetlist.
+	 * XXX: For now we only check RPR's argument. Eventually we have to
+	 * recurse the targetlist to find out all mappings in Var nodes.
+	 */
+	attnomap.attno = NIL;
+	attnomap.attnosyn = NIL;
+
+	foreach (l, node->plan.targetlist)
+	{
+		te = lfirst(l);
+		if (IsA(te->expr, WindowFunc))
+		{
+			WindowFunc	*func = (WindowFunc *)te->expr;
+			if (func->winfnoid != F_RPR)
+				continue;
+
+			/* sanity check */
+			nargs = list_length(func->args);
+			if (list_length(func->args) != 1)
+				elog(ERROR, "RPR must have 1 argument but function %d has %d args", func->winfnoid, nargs);
+
+			expr = (Expr *) lfirst(list_head(func->args));
+			if (!IsA(expr, Var))
+				elog(ERROR, "RPR's arg is not Var");
+
+			var = (Var *)expr;
+			elog(DEBUG1, "resname: %s varattno: %d varattnosyn: %d",
+				 te->resname, var->varattno, var->varattnosyn);
+			attnomap.attno = lappend_int(attnomap.attno, var->varattno);
+			attnomap.attnosyn = lappend_int(attnomap.attnosyn, var->varattnosyn);
+		}
+	}
+
+	winstate->defineVariableList = NIL;
+	winstate->defineClauseList = NIL;
+	if (node->defineClause != NIL)
+	{
+		foreach(l, node->defineClause)
+		{
+			char		*name;
+			ExprState	*exps;
+
+			te = lfirst(l);
+			name = te->resname;
+			expr = te->expr;
+
+			elog(DEBUG1, "defineVariable name: %s", name);
+			winstate->defineVariableList = lappend(winstate->defineVariableList,
+												   makeString(pstrdup(name)));
+			/* tweak expr so that it referes to outer slot */
+			attno_map((Node *)expr, &attnomap);
+			exps = ExecInitExpr(expr, (PlanState *) winstate);
+			winstate->defineClauseList = lappend(winstate->defineClauseList, exps);
+		}
+	}
+
 	winstate->all_first = true;
 	winstate->partition_spooled = false;
 	winstate->more_partitions = false;
@@ -2674,6 +2764,76 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	return winstate;
 }
 
+/*
+ * Rewrite Var node's varattno to the varattno which is used in the target
+ * list using AttnoMap.  We also rewrite varno so that it sees outer tuple
+ * (PREV) or inner tuple (NEXT).
+ */
+static void
+attno_map(Node *node, AttnoMap *map)
+{
+	(void) expression_tree_walker(node, attno_map_walker, (void *) map);
+}
+
+static bool
+attno_map_walker(Node *node, void *context)
+{
+	FuncExpr	*func;
+	int			nargs;
+	Expr		*expr;
+	Var			*var;
+	AttnoMap	*attnomap;
+	ListCell	*lc1, *lc2;
+	
+	if (node == NULL)
+		return false;
+
+	attnomap = (AttnoMap *) context;
+
+	if (IsA(node, FuncExpr))
+	{
+		func = (FuncExpr *)node;
+
+		if (func->funcid == F_PREV || func->funcid == F_NEXT)
+		{
+			/* sanity check */
+			nargs = list_length(func->args);
+			if (list_length(func->args) != 1)
+				elog(ERROR, "PREV/NEXT must have 1 argument but function %d has %d args", func->funcid, nargs);
+
+			expr = (Expr *) lfirst(list_head(func->args));
+			if (!IsA(expr, Var))
+				elog(ERROR, "PREV/NEXT's arg is not Var");	/* XXX: is it possible that arg type is Const? */
+			var = (Var *)expr;
+
+			if (func->funcid == F_PREV)
+				var->varno = OUTER_VAR;
+			else
+				var->varno = INNER_VAR;
+		}
+		return expression_tree_walker(node, attno_map_walker, (void *) context);
+	}
+	else if (IsA(node, Var))
+	{
+		var = (Var *)node;	 
+
+		elog(DEBUG1, "original varno: %d varattno: %d", var->varno, var->varattno);
+
+		forboth(lc1, attnomap->attno, lc2, attnomap->attnosyn)
+		{
+			int	attno = lfirst_int(lc1);
+			int	attnosyn = lfirst_int(lc2);
+
+			if (var->varattno == attnosyn)
+			{
+				elog(DEBUG1, "loc: %d rewrite varattno from: %d to %d", var->location, attnosyn, attno);
+				var->varattno = attno;
+			}
+		}
+	}
+	return expression_tree_walker(node, attno_map_walker, (void *) context);
+}
+
 /* -----------------
  * ExecEndWindowAgg
  * -----------------
@@ -2691,6 +2851,8 @@ ExecEndWindowAgg(WindowAggState *node)
 	ExecClearTuple(node->agg_row_slot);
 	ExecClearTuple(node->temp_slot_1);
 	ExecClearTuple(node->temp_slot_2);
+	ExecClearTuple(node->prev_slot);
+	ExecClearTuple(node->next_slot);
 	if (node->framehead_slot)
 		ExecClearTuple(node->framehead_slot);
 	if (node->frametail_slot)
@@ -2740,6 +2902,8 @@ ExecReScanWindowAgg(WindowAggState *node)
 	ExecClearTuple(node->agg_row_slot);
 	ExecClearTuple(node->temp_slot_1);
 	ExecClearTuple(node->temp_slot_2);
+	ExecClearTuple(node->prev_slot);
+	ExecClearTuple(node->next_slot);
 	if (node->framehead_slot)
 		ExecClearTuple(node->framehead_slot);
 	if (node->frametail_slot)
@@ -3080,7 +3244,7 @@ are_peers(WindowAggState *winstate, TupleTableSlot *slot1,
  *
  * Returns true if successful, false if no such row
  */
-static bool
+bool
 window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
 {
 	WindowAggState *winstate = winobj->winstate;
@@ -3420,14 +3584,53 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
 	WindowAggState *winstate;
 	ExprContext *econtext;
 	TupleTableSlot *slot;
-	int64		abs_pos;
-	int64		mark_pos;
 
 	Assert(WindowObjectIsValid(winobj));
 	winstate = winobj->winstate;
 	econtext = winstate->ss.ps.ps_ExprContext;
 	slot = winstate->temp_slot_1;
 
+	if (WinGetSlotInFrame(winobj, slot,
+						  relpos, seektype, set_mark,
+						  isnull, isout) == 0)
+	{
+		econtext->ecxt_outertuple = slot;
+		return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
+							econtext, isnull);
+	}
+
+	if (isout)
+		*isout = true;
+	*isnull = true;
+	return (Datum) 0;
+}
+
+/*
+ * WinGetSlotInFrame
+ * slot: TupleTableSlot to store the result
+ * relpos: signed rowcount offset from the seek position
+ * seektype: WINDOW_SEEK_HEAD or WINDOW_SEEK_TAIL
+ * set_mark: If the row is found/in frame and set_mark is true, the mark is
+ *		moved to the row as a side-effect.
+ * isnull: output argument, receives isnull status of result
+ * isout: output argument, set to indicate whether target row position
+ *		is out of frame (can pass NULL if caller doesn't care about this)
+ *
+ * Returns 0 if we successfullt got the slot. false if out of frame.
+ * (also isout is set)
+ */
+int
+WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot,
+					 int relpos, int seektype, bool set_mark,
+					 bool *isnull, bool *isout)
+{
+	WindowAggState *winstate;
+	int64		abs_pos;
+	int64		mark_pos;
+
+	Assert(WindowObjectIsValid(winobj));
+	winstate = winobj->winstate;
+
 	switch (seektype)
 	{
 		case WINDOW_SEEK_CURRENT:
@@ -3583,15 +3786,13 @@ WinGetFuncArgInFrame(WindowObject winobj, int argno,
 		*isout = false;
 	if (set_mark)
 		WinSetMarkPosition(winobj, mark_pos);
-	econtext->ecxt_outertuple = slot;
-	return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
-						econtext, isnull);
+	return 0;
 
 out_of_frame:
 	if (isout)
 		*isout = true;
 	*isnull = true;
-	return (Datum) 0;
+	return -1;
 }
 
 /*
@@ -3622,3 +3823,9 @@ WinGetFuncArgCurrent(WindowObject winobj, int argno, bool *isnull)
 	return ExecEvalExpr((ExprState *) list_nth(winobj->argstates, argno),
 						econtext, isnull);
 }
+
+WindowAggState *
+WinGetAggState(WindowObject winobj)
+{
+	return winobj->winstate;
+}
diff --git a/src/backend/utils/adt/windowfuncs.c b/src/backend/utils/adt/windowfuncs.c
index b87a624fb2..74ef11ce55 100644
--- a/src/backend/utils/adt/windowfuncs.c
+++ b/src/backend/utils/adt/windowfuncs.c
@@ -13,6 +13,9 @@
  */
 #include "postgres.h"
 
+#include "catalog/pg_collation_d.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
 #include "nodes/supportnodes.h"
 #include "utils/builtins.h"
 #include "windowapi.h"
@@ -36,10 +39,21 @@ typedef struct
 	int64		remainder;		/* (total rows) % (bucket num) */
 } ntile_context;
 
+/*
+ * rpr process information.
+ * Used for AFTER MATCH SKIP PAST LAST ROW
+ */
+typedef struct SkipContext
+{
+	int64		pos;	/* last row absolute position */
+} SkipContext;
+
 static bool rank_up(WindowObject winobj);
 static Datum leadlag_common(FunctionCallInfo fcinfo,
 							bool forward, bool withoffset, bool withdefault);
-
+static bool get_slots(WindowObject winobj, WindowAggState *winstate, int current_pos);
+static int evaluate_pattern(WindowObject winobj, WindowAggState *winstate,
+							int relpos, char *vname, char *quantifier, bool *result);
 
 /*
  * utility routine for *_rank functions.
@@ -713,3 +727,289 @@ window_nth_value(PG_FUNCTION_ARGS)
 
 	PG_RETURN_DATUM(result);
 }
+
+/*
+ * rpr
+ * allow to use "Row pattern recognition: WINDOW clause" (SQL:2016 R020) in
+ * the target list.
+ * Usage: SELECT rpr(colname) OVER (..)
+ * where colname is defined in PATTERN clause.
+ */
+Datum
+window_rpr(PG_FUNCTION_ARGS)
+{
+#define	MAX_PATTERNS	16	/* max variables in PATTERN clause */
+
+	WindowObject winobj = PG_WINDOW_OBJECT();
+	WindowAggState	*winstate = WinGetAggState(winobj);
+	Datum		result;
+	bool		expression_result;
+	bool		isnull;
+	int			relpos;
+	int64		curr_pos, markpos;
+	ListCell	*lc, *lc1;
+	SkipContext	*context = NULL;
+
+	curr_pos = WinGetCurrentPosition(winobj);
+	elog(DEBUG1, "rpr is called. row: " INT64_FORMAT, curr_pos);
+
+	if (winstate->rpSkipTo == ST_PAST_LAST_ROW)
+	{
+		context = (SkipContext *) WinGetPartitionLocalMemory(winobj, sizeof(SkipContext));
+		if (curr_pos < context->pos)
+		{
+			elog(DEBUG1, "skip this row: curr_pos: " INT64_FORMAT "context->pos: " INT64_FORMAT,
+				 curr_pos, context->pos);
+			PG_RETURN_NULL();
+		}
+	}
+
+	/*
+	 * Evaluate PATTERN until one of expressions is not true or out of frame.
+	 */
+	relpos = 0;
+
+	forboth(lc, winstate->patternVariableList, lc1, winstate->patternRegexpList)
+	{
+		char	*vname = strVal(lfirst(lc));
+		char	*quantifier = strVal(lfirst(lc1));
+
+		elog(DEBUG1, "relpos: %d pattern vname: %s quantifier: %s", relpos, vname, quantifier);
+
+		/* evaluate row pattern against current row */
+		relpos = evaluate_pattern(winobj, winstate, relpos, vname, quantifier, &expression_result);
+
+		/*
+		 * If the expression did not match, we are done.
+		 */
+		if (!expression_result)
+			break;
+
+		/* out of frame? */
+		if (relpos < 0)
+			break;
+
+		/* count up relative row position */
+		relpos++;
+	}
+
+	elog(DEBUG1, "relpos: %d", relpos);
+
+	/*
+	 * If current row satified the pattern, return argument expression.
+	 */
+	if (expression_result)
+	{
+		result = WinGetFuncArgInFrame(winobj, 0,
+									  0, WINDOW_SEEK_HEAD, false,
+									  &isnull, NULL);
+	}
+
+	/*
+	 * At this point we can set mark down to current pos -2.
+	 */
+	markpos = curr_pos -2;
+	elog(DEBUG1, "markpos: " INT64_FORMAT, markpos);
+	if (markpos >= 0)
+		WinSetMarkPosition(winobj, markpos);
+
+	if (expression_result)
+	{
+		if (winstate->rpSkipTo == ST_PAST_LAST_ROW)
+		{
+			context->pos += relpos;
+			elog(DEBUG1, "context->pos: " INT64_FORMAT, context->pos);
+		}
+		PG_RETURN_DATUM(result);
+	}
+
+	PG_RETURN_NULL();
+}
+
+/*
+ * Evaluate expression associated with PATTERN variable vname.
+ * relpos is relative row position in a frame (starting from 0).
+ * "quantifier" is the quatifier part of the PATTERN regular expression.
+ * Currently only '+' is allowed.
+ * result is out paramater representing the expression evaluation result
+ * is true of false.
+ * Return values are:
+ * >=0: the last match relative row position
+ * -1: current row is out of frame
+ */
+static
+int evaluate_pattern(WindowObject winobj, WindowAggState *winstate,
+					 int relpos, char *vname, char *quantifier, bool *result)
+{
+	ExprContext	*econtext = winstate->ss.ps.ps_ExprContext;
+	ListCell	*lc1, *lc2;
+	ExprState	*pat;
+	Datum		eval_result;
+	int			sts;
+	bool		out_of_frame = false;
+	bool		isnull;
+	StringInfo	encoded_str = makeStringInfo();
+	char		pattern_str[128];
+
+	forboth (lc1, winstate->defineVariableList, lc2, winstate->defineClauseList)
+	{
+		char	*name = strVal(lfirst(lc1));
+		bool	second_try_match = false;
+
+		if (strcmp(vname, name))
+			continue;
+
+		/* set expression to evaluate */
+		pat = lfirst(lc2);
+
+		for (;;)
+		{
+			if (!get_slots(winobj, winstate, relpos))
+			{
+				out_of_frame = true;
+				break;	/* current row is out of frame */
+			}
+
+			/* evaluate the expression */
+			eval_result = ExecEvalExpr(pat, econtext, &isnull);
+			if (isnull)
+			{
+				/* expression is NULL */
+				elog(DEBUG1, "expression for %s is NULL at row: %d", vname, relpos);
+				break;
+			}
+			else
+			{
+				if (!DatumGetBool(eval_result))
+				{
+					/* expression is false */
+					elog(DEBUG1, "expression for %s is false at row: %d", vname, relpos);
+					break;
+				}
+				else
+				{
+					/* expression is true */
+					elog(DEBUG1, "expression for %s is true at row: %d", vname, relpos);
+					appendStringInfoChar(encoded_str, vname[0]);
+
+					/* If quantifier is "+", we need to look for more matching row */
+					if (quantifier && !strcmp(quantifier, "+"))
+					{
+						/* remember that we want to try another row */
+						second_try_match = true;
+						relpos++;
+					}
+					else
+						break;
+				}
+			}
+		}
+		if (second_try_match)
+			relpos--;
+
+		if (out_of_frame)
+		{
+			*result = false;
+			return -1;
+		}
+
+		/* build regular expression */
+		snprintf(pattern_str, sizeof(pattern_str), "%c%s", vname[0], quantifier);
+
+		/*
+		 * Do regular expression matching against sequence of rows satisfying
+		 * the expression using regexp_instr().
+		 */
+		sts = DatumGetInt32(DirectFunctionCall2Coll(regexp_instr, DEFAULT_COLLATION_OID,
+													PointerGetDatum(cstring_to_text(encoded_str->data)),
+													PointerGetDatum(cstring_to_text(pattern_str))));
+		elog(DEBUG1, "regexp_instr returned: %d. str: %s regexp: %s",
+			 sts, encoded_str->data, pattern_str);
+		*result = (sts > 0)? true : false;
+	}
+	return relpos;
+}
+
+/*
+ * Get current, previous and next tuple.
+ * Returns true if still within frame.
+ */
+static bool
+get_slots(WindowObject winobj, WindowAggState *winstate, int current_pos)
+{
+	TupleTableSlot *slot;
+	bool	isnull, isout;
+	int		sts;
+	ExprContext *econtext;
+
+	econtext = winstate->ss.ps.ps_ExprContext;
+
+	/* for current row */
+	slot = winstate->temp_slot_1;
+	sts = WinGetSlotInFrame(winobj, slot,
+							current_pos, WINDOW_SEEK_HEAD, false,
+							&isnull, &isout);
+	if (sts < 0)
+	{
+		elog(DEBUG1, "current row is out of frame");
+		econtext->ecxt_scantuple = winstate->null_slot;
+		return false;
+	}
+	else
+		econtext->ecxt_scantuple = slot;
+
+	/* for PREV */
+	if (current_pos > 0)
+	{
+		slot = winstate->prev_slot;
+		sts = WinGetSlotInFrame(winobj, slot,
+								current_pos - 1, WINDOW_SEEK_HEAD, false,
+								&isnull, &isout);
+		if (sts < 0)
+		{
+			elog(DEBUG1, "previous row out of frame at: %d", current_pos);
+			econtext->ecxt_outertuple = winstate->null_slot;
+		}
+		econtext->ecxt_outertuple = slot;
+	}
+	else
+		econtext->ecxt_outertuple = winstate->null_slot;
+
+	/* for NEXT */
+	slot = winstate->next_slot;
+	sts = WinGetSlotInFrame(winobj, slot,
+							current_pos + 1, WINDOW_SEEK_HEAD, false,
+							&isnull, &isout);
+	if (sts < 0)
+	{
+		elog(DEBUG1, "next row out of frame at: %d", current_pos);
+		econtext->ecxt_innertuple = winstate->null_slot;
+	}
+	else
+		econtext->ecxt_innertuple = slot;
+
+	return true;
+}
+
+/*
+ * prev
+ * Dummy function to invoke RPR's navigation operator "PREV".
+ * This is *not* a window function.
+ */
+Datum
+window_prev(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+}
+
+/*
+ * next
+ * Dummy function to invoke RPR's navigation operation "NEXT".
+ * This is *not* a window function.
+ */
+Datum
+window_next(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+}
+
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6996073989..e3a9e0ffeb 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -10397,6 +10397,15 @@
 { oid => '3114', descr => 'fetch the Nth row value',
   proname => 'nth_value', prokind => 'w', prorettype => 'anyelement',
   proargtypes => 'anyelement int4', prosrc => 'window_nth_value' },
+{ oid => '6122', descr => 'row pattern recognition in window',
+  proname => 'rpr', prokind => 'w', prorettype => 'anyelement',
+  proargtypes => 'anyelement', prosrc => 'window_rpr' },
+{ oid => '6123', descr => 'previous value',
+  proname => 'prev', provolatile => 's', prorettype => 'anyelement',
+  proargtypes => 'anyelement', prosrc => 'window_prev' },
+{ oid => '6124', descr => 'next value',
+  proname => 'next', provolatile => 's', prorettype => 'anyelement',
+  proargtypes => 'anyelement', prosrc => 'window_next' },
 
 # functions for range types
 { oid => '3832', descr => 'I/O',
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index cb714f4a19..1643eaa6f1 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -2519,6 +2519,14 @@ typedef struct WindowAggState
 	int64		groupheadpos;	/* current row's peer group head position */
 	int64		grouptailpos;	/* " " " " tail position (group end+1) */
 
+	/* these fields are used in Row pattern recognition: */
+	RPSkipTo	rpSkipTo;		/* Row Pattern Skip To type */	
+	List	   *patternVariableList;	/* list of row pattern variables names (list of String) */
+	List	   *patternRegexpList;	/* list of row pattern regular expressions ('+' or ''. list of String) */
+	List	   *defineVariableList;	/* list of row pattern definition variables (list of String) */
+	List	   *defineClauseList;	/* expression for row pattern definition
+									 * search conditions ExprState list */
+
 	MemoryContext partcontext;	/* context for partition-lifespan data */
 	MemoryContext aggcontext;	/* shared context for aggregate working data */
 	MemoryContext curaggcontext;	/* current aggregate's working data */
@@ -2555,6 +2563,11 @@ typedef struct WindowAggState
 	TupleTableSlot *agg_row_slot;
 	TupleTableSlot *temp_slot_1;
 	TupleTableSlot *temp_slot_2;
+
+	/* temporary slots for RPR */
+	TupleTableSlot *prev_slot;	/* PREV row navigation operator */
+	TupleTableSlot *next_slot;	/* NEXT row navigation operator */
+	TupleTableSlot *null_slot;	/* all NULL slot */
 } WindowAggState;
 
 /* ----------------
diff --git a/src/include/windowapi.h b/src/include/windowapi.h
index b8c2c565d1..a0facf38fe 100644
--- a/src/include/windowapi.h
+++ b/src/include/windowapi.h
@@ -58,7 +58,16 @@ extern Datum WinGetFuncArgInFrame(WindowObject winobj, int argno,
 								  int relpos, int seektype, bool set_mark,
 								  bool *isnull, bool *isout);
 
+extern int WinGetSlotInFrame(WindowObject winobj, TupleTableSlot *slot,
+							 int relpos, int seektype, bool set_mark,
+							 bool *isnull, bool *isout);
+
 extern Datum WinGetFuncArgCurrent(WindowObject winobj, int argno,
 								  bool *isnull);
 
+extern WindowAggState *WinGetAggState(WindowObject winobj);
+
+extern bool window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot);
+
+
 #endif							/* WINDOWAPI_H */
-- 
2.25.1


----Next_Part(Mon_Jun_26_17_45_07_2023_724)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="v2-0005-Row-pattern-recognition-patch-docs.patch"



^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:41 Andrey Rachitskiy <[email protected]>
  2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <[email protected]>
  0 siblings, 1 reply; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:41 UTC (permalink / raw)
  To: ; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]>



Hi, Hackers!

When a PL/Perl function returns a large text value, sv2cstr() copies the
entire Perl string into backend memory with no size check.  The helper
is used on the path from Perl return values and SPI arguments to
PostgreSQL text datums; it simply palloc()s a copy after SvPVutf8().
A user who is allowed to create untrusted PL/Perl functions can
therefore force the backend to allocate strings far larger than any
session limit. On a memory-constrained host this can get the backend
process killed by the OOM killer (SIGKILL) rather than raising a
catchable PostgreSQL error.

Reproducer (unpatched master, plperl enabled):

CREATE FUNCTION perl_huge_text() RETURNS text LANGUAGE plperl
AS $$ return 'x' x (1024 * 1024 * 1024);
  $$;
  SELECT perl_huge_text();

On a container limited to about 768MB RAM, CREATE FUNCTION alone is
enough to lose the backend:

  LOG:  client backend (PID ...) was terminated by signal 9: Killed
  DETAIL:  Failed process was running: CREATE OR REPLACE FUNCTION ...

With plenty of free RAM the same code may succeed instead, which I think
shows missing enforcement rather than an intentional "no limit" design:
other PL/Perl paths already enforce bounds (MAXDIM, AV_SIZE_MAX for SPI
results, max_stack_depth in recursive conversion), but sv2cstr() had
none.

This patch rejects Perl strings larger than work_mem * 1024 bytes,
capped by MaxAllocSize, before copying them through sv2cstr().  That
follows the same work_mem-based pattern used elsewhere in the backend
for per-query working storage.  The check is done after SvPVutf8() has
reported the length but before utf_u2e() allocates the
database-encoding copy. A plperl regression test returns a 16MB string
with the default 4MB work_mem and expects:

  ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
  HINT:  Increase work_mem or reduce the result size.

Legitimate functions that need to move more data can raise work_mem for
the session, consistent with other operations bounded by that GUC.

Comments welcome.

-- 
Regards,
Andrey Rachitskiy



^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
  2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
@ 2026-07-07 01:56 ` Tom Lane <[email protected]>
  2026-07-07 05:44   ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
  0 siblings, 1 reply; 271+ messages in thread

From: Tom Lane @ 2026-07-07 01:56 UTC (permalink / raw)
  To: Andrey Rachitskiy <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]>

Andrey Rachitskiy <[email protected]> writes:
> When a PL/Perl function returns a large text value, sv2cstr() copies the
> entire Perl string into backend memory with no size check.  The helper
> is used on the path from Perl return values and SPI arguments to
> PostgreSQL text datums; it simply palloc()s a copy after SvPVutf8().
> A user who is allowed to create untrusted PL/Perl functions can
> therefore force the backend to allocate strings far larger than any
> session limit. On a memory-constrained host this can get the backend
> process killed by the OOM killer (SIGKILL) rather than raising a
> catchable PostgreSQL error.

This is true of very many operations in PG, not only PL/Perl.
Our general answer to that is to disable memory overcommit
so that the OOM killer won't apply.  One should also note that
the same PL/Perl function can (try to) allocate enormous amounts
of memory entirely within Perl, where we have no ability to stop
it.  I don't see how constraining the size of a function result
string helps noticeably.

> This patch rejects Perl strings larger than work_mem * 1024 bytes,

Our normal understanding of work_mem is that it's a point beyond which
we'll spill to disk, or otherwise try to reduce our memory consumption
at the cost of longer runtime.  Not a point at which an outright query
failure is OK.

So, even if I thought this were something we should address,
I don't believe this is an appropriate approach to a fix.

			regards, tom lane





^ permalink  raw  reply  [nested|flat] 271+ messages in thread

* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
  2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
  2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <[email protected]>
@ 2026-07-07 05:44   ` Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 271+ messages in thread

From: Andrey Rachitskiy @ 2026-07-07 05:44 UTC (permalink / raw)
  To: Tom Lane <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]>

Thanks for the review, Tom.

You're right that work_mem is a poor fit for a hard failure here, and
more generally that this isn't the sort of problem PL/Perl can solve
with a small boundary check alone.  I should have raised the idea on
the list for discussion before sending a patch — I'll do that next time
rather than charging ahead with a fix.

Thanks for the feedback.


On Mon, 06 Jul 2026 21:56:17 -0400, Tom Lane <[email protected]> wrote:

> Andrey Rachitskiy <[email protected]> writes:
> > When a PL/Perl function returns a large text value, sv2cstr()
> > copies the entire Perl string into backend memory with no size
> > check.  The helper is used on the path from Perl return values and
> > SPI arguments to PostgreSQL text datums; it simply palloc()s a copy
> > after SvPVutf8(). A user who is allowed to create untrusted PL/Perl
> > functions can therefore force the backend to allocate strings far
> > larger than any session limit. On a memory-constrained host this
> > can get the backend process killed by the OOM killer (SIGKILL)
> > rather than raising a catchable PostgreSQL error.
> 
> This is true of very many operations in PG, not only PL/Perl.
> Our general answer to that is to disable memory overcommit
> so that the OOM killer won't apply.  One should also note that
> the same PL/Perl function can (try to) allocate enormous amounts
> of memory entirely within Perl, where we have no ability to stop
> it.  I don't see how constraining the size of a function result
> string helps noticeably.
> 
> > This patch rejects Perl strings larger than work_mem * 1024 bytes,
> 
> Our normal understanding of work_mem is that it's a point beyond which
> we'll spill to disk, or otherwise try to reduce our memory consumption
> at the cost of longer runtime.  Not a point at which an outright query
> failure is OK.
> 
> So, even if I thought this were something we should address,
> I don't believe this is an appropriate approach to a fix.
> 
> 			regards, tom lane



-- 
Regards,
Andrey Rachitskiy







^ permalink  raw  reply  [nested|flat] 271+ messages in thread


end of thread, other threads:[~2026-07-07 05:44 UTC | newest]

Thread overview: 271+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2023-06-26 08:05 [PATCH v2 4/7] Row pattern recognition patch (executor). Tatsuo Ishii <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <[email protected]>
2026-07-07 05:44   ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox