agora inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH v50 4/8] Fix a few problems in index build progress reporting.
254+ messages / 3 participants
[nested] [flat]

* [PATCH v50 4/8] Fix a few problems in index build progress reporting.
@ 2026-03-27 15:50  Álvaro Herrera <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Álvaro Herrera @ 2026-03-27 15:50 UTC (permalink / raw)

First, index_build() should not update the progress when being driven by
REPACK, because the progress reporting infractructure cannot handle status of
two commands at the same time. So far, REPACK with the CONCURRENTLY option
neglected this problem altogether, but even the existing REPACK wasn't
consistent enough: even if the 'progress' variable in repack_index() was
false, it didn't pass the value to index_build().

Second, REPACK (CONCURRENTLY) should not set PROGRESS_REPACK_PHASE to
PROGRESS_REPACK_PHASE_FINAL_CLEANUP in rebuild_relation() because it calls
finish_heap_swap() anyway (via rebuild_relation_finish_concurrent()), which
does the same thing.
---
 src/backend/bootstrap/bootstrap.c |  2 +-
 src/backend/catalog/heap.c        |  3 ++-
 src/backend/catalog/index.c       | 22 ++++++++++++++++++----
 src/backend/catalog/toasting.c    |  3 ++-
 src/backend/commands/indexcmds.c  |  1 +
 src/include/catalog/index.h       |  4 +++-
 6 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 38ef683d4c7..60fb7051830 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1183,7 +1183,7 @@ build_indices(void)
 		heap = table_open(ILHead->il_heap, NoLock);
 		ind = index_open(ILHead->il_ind, NoLock);
 
-		index_build(heap, ind, ILHead->il_info, false, false);
+		index_build(heap, ind, ILHead->il_info, false, false, false);
 
 		index_close(ind, NoLock);
 		table_close(heap, NoLock);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 5748aa9a1a9..ae6b7cda3dd 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3570,7 +3570,8 @@ RelationTruncateIndexes(Relation heapRelation)
 
 		/* Initialize the index and rebuild */
 		/* Note: we do not need to re-establish pkey setting */
-		index_build(heapRelation, currentIndex, indexInfo, true, false);
+		index_build(heapRelation, currentIndex, indexInfo, true, false,
+					true);
 
 		/* We're done with this index */
 		index_close(currentIndex, NoLock);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index e418d67e8e4..4b8ed2c7660 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -715,6 +715,9 @@ UpdateIndexRelation(Oid indexoid,
  *			already exists.
  *		INDEX_CREATE_PARTITIONED:
  *			create a partitioned index (table must be partitioned)
+ *		INDEX_CREATE_REPORT_PROGRESS:
+ *			update the backend's progress information during index build.
+
  * constr_flags: flags passed to index_constraint_create
  *		(only if INDEX_CREATE_ADD_CONSTRAINT is set)
  * allow_system_table_mods: allow table to be a system catalog
@@ -760,6 +763,7 @@ index_create(Relation heapRelation,
 	bool		invalid = (flags & INDEX_CREATE_INVALID) != 0;
 	bool		concurrent = (flags & INDEX_CREATE_CONCURRENT) != 0;
 	bool		partitioned = (flags & INDEX_CREATE_PARTITIONED) != 0;
+	bool		progress = (flags & INDEX_CREATE_REPORT_PROGRESS) != 0;
 	char		relkind;
 	TransactionId relfrozenxid;
 	MultiXactId relminmxid;
@@ -1276,7 +1280,8 @@ index_create(Relation heapRelation,
 	}
 	else
 	{
-		index_build(heapRelation, indexRelation, indexInfo, false, true);
+		index_build(heapRelation, indexRelation, indexInfo, false, true,
+					progress);
 	}
 
 	/*
@@ -1448,6 +1453,12 @@ index_create_copy(Relation heapRelation, bool concurrently,
 		stattargets[i].isnull = isnull;
 	}
 
+	/*
+	 * Note: The current callers do not need INDEX_CREATE_REPORT_PROGRESS. If
+	 * 'concurrently' is true, there is no build at all. Otherwise the index
+	 * build is a sub-command of REPACK. The current infrastructure does not
+	 * allow two commands to report their progress at the same time.
+	 */
 	if (concurrently)
 		flags = INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT;
 
@@ -1538,7 +1549,7 @@ index_concurrently_build(Oid heapRelationId,
 	indexInfo->ii_BrokenHotChain = false;
 
 	/* Now build the index */
-	index_build(heapRel, indexRelation, indexInfo, false, true);
+	index_build(heapRel, indexRelation, indexInfo, false, true, true);
 
 	/* Roll back any GUC changes executed by index functions */
 	AtEOXact_GUC(false, save_nestlevel);
@@ -3009,6 +3020,7 @@ index_update_stats(Relation rel,
  *
  * isreindex indicates we are recreating a previously-existing index.
  * parallel indicates if parallelism may be useful.
+ * progress indicates if the backend should update its progress info.
  *
  * Note: before Postgres 8.2, the passed-in heap and index Relations
  * were automatically closed by this routine.  This is no longer the case.
@@ -3019,7 +3031,8 @@ index_build(Relation heapRelation,
 			Relation indexRelation,
 			IndexInfo *indexInfo,
 			bool isreindex,
-			bool parallel)
+			bool parallel,
+			bool progress)
 {
 	IndexBuildResult *stats;
 	Oid			save_userid;
@@ -3070,6 +3083,7 @@ index_build(Relation heapRelation,
 	RestrictSearchPath();
 
 	/* Set up initial progress report status */
+	if (progress)
 	{
 		const int	progress_index[] = {
 			PROGRESS_CREATEIDX_PHASE,
@@ -3827,7 +3841,7 @@ reindex_index(const ReindexStmt *stmt, Oid indexId,
 
 	/* Initialize the index and rebuild */
 	/* Note: we do not need to re-establish pkey setting */
-	index_build(heapRelation, iRel, indexInfo, true, true);
+	index_build(heapRelation, iRel, indexInfo, true, true, progress);
 
 	/* Re-allow use of target index */
 	ResetReindexProcessing();
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 4aa52a4bd25..51b27a8c71c 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -332,7 +332,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 				 BTREE_AM_OID,
 				 rel->rd_rel->reltablespace,
 				 collationIds, opclassIds, NULL, coloptions, NULL, (Datum) 0,
-				 INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL);
+				 INDEX_CREATE_IS_PRIMARY | INDEX_CREATE_REPORT_PROGRESS, 0,
+				 true, true, NULL);
 
 	table_close(toast_rel, NoLock);
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index cba379810c7..932924c13e0 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1231,6 +1231,7 @@ DefineIndex(ParseState *pstate,
 		flags |= INDEX_CREATE_PARTITIONED;
 	if (stmt->primary)
 		flags |= INDEX_CREATE_IS_PRIMARY;
+	flags |= INDEX_CREATE_REPORT_PROGRESS;
 
 	/*
 	 * If the table is partitioned, and recursion was declined but partitions
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index ed9e4c37d27..7ebe4f0bd87 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -71,6 +71,7 @@ extern void index_check_primary_key(Relation heapRel,
 #define	INDEX_CREATE_IF_NOT_EXISTS			(1 << 4)
 #define	INDEX_CREATE_PARTITIONED			(1 << 5)
 #define INDEX_CREATE_INVALID				(1 << 6)
+#define INDEX_CREATE_REPORT_PROGRESS		(1 << 7)
 
 extern Oid	index_create(Relation heapRelation,
 						 const char *indexRelationName,
@@ -148,7 +149,8 @@ extern void index_build(Relation heapRelation,
 						Relation indexRelation,
 						IndexInfo *indexInfo,
 						bool isreindex,
-						bool parallel);
+						bool parallel,
+						bool progress);
 
 extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
 
-- 
2.47.3


--brnevsqjnuzpyok4
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
	filename="v50-0005-support-repacking-tables-with-exclusion-constrai.patch"



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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

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

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

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

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

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

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





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

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

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13  Andrey Rachitskiy <[email protected]>
  0 siblings, 0 replies; 254+ messages in thread

From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)

When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check.  A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.

Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr().  This follows the
same work_mem-based limit pattern used elsewhere in the backend.

Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.

Author: Andrey Rachitskiy <[email protected]>
---
 src/pl/plperl/expected/plperl.out |  8 +++++++
 src/pl/plperl/plperl.h            | 34 ++++++++++++++++++++++++++++++
 src/pl/plperl/sql/plperl.sql      |  7 ++++++
 3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
          126
 (1 row)
 
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR:  Perl value exceeds maximum allowed size (4194304 bytes)
+HINT:  Increase work_mem or reduce the result size.
+CONTEXT:  PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
 
 /* defines free() by way of system headers, so must be included before perl.h */
 #include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
 
 /*
  * Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char	   *plperl_sv_to_literal(SV *, char *);
 void		plperl_util_elog(int level, SV *msg);
 
 
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+	Size		limit = (Size) work_mem * (Size) 1024;
+
+	return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+	Size		max_len = plperl_max_scalar_bytes();
+
+	if ((Size) len > max_len)
+		erereport(ERROR,
+				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+				 errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+						max_len),
+			 errhint("Increase work_mem or reduce the result size.")));
+}
+
 /* helper functions */
 
 /*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
 	else
 		val = SvPVutf8(sv, len);
 
+	plperl_check_sv_length(len);
+
 	/*
 	 * Now convert to database encoding.  We use perl's length in the event we
 	 * had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
 
 SELECT self_modify(42);
 SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+	return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();

--MP_/sDTAhto+gkG/PMs8Alw6m8e--





^ permalink  raw  reply  [nested|flat] 254+ messages in thread

* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:41  Andrey Rachitskiy <[email protected]>
  0 siblings, 1 reply; 254+ 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] 254+ messages in thread

* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-07 01:56  Tom Lane <[email protected]>
  parent: Andrey Rachitskiy <[email protected]>
  0 siblings, 1 reply; 254+ 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] 254+ messages in thread

* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-07 05:44  Andrey Rachitskiy <[email protected]>
  parent: Tom Lane <[email protected]>
  0 siblings, 0 replies; 254+ 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] 254+ messages in thread


end of thread, other threads:[~2026-07-07 05:44 UTC | newest]

Thread overview: 254+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-03-27 15:50 [PATCH v50 4/8] Fix a few problems in index build progress reporting. Álvaro Herrera <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[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