agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH v3 1/3] Define DDL replication levels via the CREATE PUBLICATION command, currently allow Database or Table level DDL replication.
331+ messages / 3 participants
[nested] [flat]
* [PATCH v3 1/3] Define DDL replication levels via the CREATE PUBLICATION command, currently allow Database or Table level DDL replication.
@ 2022-03-18 16:47 Zheng (Zane) Li <zhelli@amazon.com>
0 siblings, 0 replies; 331+ messages in thread
From: Zheng (Zane) Li @ 2022-03-18 16:47 UTC (permalink / raw)
Allow the user to configure either database level or table level
DDL replication via the CREATE PUBLICATION command as in my first
email. Two new columns are added to the pg_publication catalog to
show the DDL replication levels, test output publication.out is
updated accordingly. pg_dump is also modified to accommodate the
pg_publication catalog change.
---
src/backend/catalog/pg_publication.c | 2 +
src/backend/commands/publicationcmds.c | 89 +++++-
src/backend/utils/cache/relcache.c | 2 +
src/bin/pg_dump/pg_dump.c | 45 ++-
src/bin/pg_dump/pg_dump.h | 2 +
src/bin/pg_dump/t/002_pg_dump.pl | 10 +-
src/bin/psql/describe.c | 30 +-
src/include/catalog/pg_publication.h | 10 +
src/include/commands/defrem.h | 1 +
src/test/regress/expected/publication.out | 372 +++++++++++-----------
10 files changed, 364 insertions(+), 199 deletions(-)
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 789b895db8..00b5673b8f 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -857,6 +857,8 @@ GetPublication(Oid pubid)
pub->pubactions.pubdelete = pubform->pubdelete;
pub->pubactions.pubtruncate = pubform->pubtruncate;
pub->pubviaroot = pubform->pubviaroot;
+ pub->pubactions.pubddl_database = pubform->pubddl_database;
+ pub->pubactions.pubddl_table = pubform->pubddl_table;
ReleaseSysCache(tup);
diff --git a/src/backend/commands/publicationcmds.c b/src/backend/commands/publicationcmds.c
index 1aad2e769c..34bfd3dcdb 100644
--- a/src/backend/commands/publicationcmds.c
+++ b/src/backend/commands/publicationcmds.c
@@ -80,15 +80,18 @@ static void PublicationDropSchemas(Oid pubid, List *schemas, bool missing_ok);
static void
parse_publication_options(ParseState *pstate,
List *options,
+ bool for_all_tables,
bool *publish_given,
PublicationActions *pubactions,
bool *publish_via_partition_root_given,
- bool *publish_via_partition_root)
+ bool *publish_via_partition_root,
+ bool *ddl_level_given)
{
ListCell *lc;
*publish_given = false;
*publish_via_partition_root_given = false;
+ *ddl_level_given = false;
/* defaults */
pubactions->pubinsert = true;
@@ -96,6 +99,16 @@ parse_publication_options(ParseState *pstate,
pubactions->pubdelete = true;
pubactions->pubtruncate = true;
*publish_via_partition_root = false;
+ if (for_all_tables)
+ {
+ pubactions->pubddl_database = true;
+ pubactions->pubddl_table = true;
+ }
+ else
+ {
+ pubactions->pubddl_database = false;
+ pubactions->pubddl_table = false;
+ }
/* Parse options */
foreach(lc, options)
@@ -154,6 +167,55 @@ parse_publication_options(ParseState *pstate,
*publish_via_partition_root_given = true;
*publish_via_partition_root = defGetBoolean(defel);
}
+ else if (strcmp(defel->defname, "ddl") == 0)
+ {
+ char *ddl_level;
+ List *ddl_list;
+ ListCell *lc;
+
+ if (*ddl_level_given)
+ errorConflictingDefElem(defel, pstate);
+
+ /*
+ * If publish option was given only the explicitly listed actions
+ * should be published.
+ */
+ pubactions->pubddl_database = false;
+ pubactions->pubddl_table = false;
+
+ *ddl_level_given = true;
+ ddl_level = defGetString(defel);
+
+ if (!SplitIdentifierString(ddl_level, ',', &ddl_list))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid list syntax for \"ddl\" option")));
+
+ /* Process the option list. */
+ foreach(lc, ddl_list)
+ {
+ char *publish_opt = (char *) lfirst(lc);
+
+ if (strcmp(publish_opt, "database") == 0)
+ {
+ if (!for_all_tables)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("DDL replication on the database level is only supported in FOR ALL TABLES publication")));
+ else
+ {
+ pubactions->pubddl_database = true;
+ pubactions->pubddl_table = true;
+ }
+ }
+ else if (strcmp(publish_opt, "table") == 0)
+ pubactions->pubddl_table = true;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unrecognized \"ddl\" value: \"%s\"", publish_opt)));
+ }
+ }
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -622,6 +684,7 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
Datum values[Natts_pg_publication];
HeapTuple tup;
bool publish_given;
+ bool ddl_level_given;
PublicationActions pubactions;
bool publish_via_partition_root_given;
bool publish_via_partition_root;
@@ -664,9 +727,11 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
parse_publication_options(pstate,
stmt->options,
+ stmt->for_all_tables,
&publish_given, &pubactions,
&publish_via_partition_root_given,
- &publish_via_partition_root);
+ &publish_via_partition_root,
+ &ddl_level_given);
puboid = GetNewOidWithIndex(rel, PublicationObjectIndexId,
Anum_pg_publication_oid);
@@ -683,6 +748,10 @@ CreatePublication(ParseState *pstate, CreatePublicationStmt *stmt)
BoolGetDatum(pubactions.pubtruncate);
values[Anum_pg_publication_pubviaroot - 1] =
BoolGetDatum(publish_via_partition_root);
+ values[Anum_pg_publication_pubddl_database - 1] =
+ BoolGetDatum(pubactions.pubddl_database);
+ values[Anum_pg_publication_pubddl_table - 1] =
+ BoolGetDatum(pubactions.pubddl_table);
tup = heap_form_tuple(RelationGetDescr(rel), values, nulls);
@@ -766,6 +835,7 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
bool replaces[Natts_pg_publication];
Datum values[Natts_pg_publication];
bool publish_given;
+ bool ddl_level_given;
PublicationActions pubactions;
bool publish_via_partition_root_given;
bool publish_via_partition_root;
@@ -774,11 +844,15 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
List *root_relids = NIL;
ListCell *lc;
+ pubform = (Form_pg_publication) GETSTRUCT(tup);
+
parse_publication_options(pstate,
stmt->options,
+ pubform->puballtables,
&publish_given, &pubactions,
&publish_via_partition_root_given,
- &publish_via_partition_root);
+ &publish_via_partition_root,
+ &ddl_level_given);
pubform = (Form_pg_publication) GETSTRUCT(tup);
@@ -859,6 +933,15 @@ AlterPublicationOptions(ParseState *pstate, AlterPublicationStmt *stmt,
replaces[Anum_pg_publication_pubtruncate - 1] = true;
}
+ if (ddl_level_given)
+ {
+ values[Anum_pg_publication_pubddl_database - 1] = BoolGetDatum(pubactions.pubddl_database);
+ replaces[Anum_pg_publication_pubddl_database - 1] = true;
+
+ values[Anum_pg_publication_pubddl_table - 1] = BoolGetDatum(pubactions.pubddl_table);
+ replaces[Anum_pg_publication_pubddl_table - 1] = true;
+ }
+
if (publish_via_partition_root_given)
{
values[Anum_pg_publication_pubviaroot - 1] = BoolGetDatum(publish_via_partition_root);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index fbd11883e1..97abe2b4d4 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -5625,6 +5625,8 @@ RelationBuildPublicationDesc(Relation relation, PublicationDesc *pubdesc)
pubdesc->pubactions.pubupdate |= pubform->pubupdate;
pubdesc->pubactions.pubdelete |= pubform->pubdelete;
pubdesc->pubactions.pubtruncate |= pubform->pubtruncate;
+ pubdesc->pubactions.pubddl_database |= pubform->pubddl_database;
+ pubdesc->pubactions.pubddl_table |= pubform->pubddl_table;
/*
* Check if all columns referenced in the filter expression are part of
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e5816c4cce..5777aa7eec 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3819,6 +3819,8 @@ getPublications(Archive *fout, int *numPublications)
int i_pubdelete;
int i_pubtruncate;
int i_pubviaroot;
+ int i_pubddl_database;
+ int i_pubddl_table;
int i,
ntups;
@@ -3833,23 +3835,29 @@ getPublications(Archive *fout, int *numPublications)
resetPQExpBuffer(query);
/* Get the publications. */
- if (fout->remoteVersion >= 130000)
+ if (fout->remoteVersion >= 150000)
+ appendPQExpBuffer(query,
+ "SELECT p.tableoid, p.oid, p.pubname, "
+ "p.pubowner, "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot, p.pubddl_database, p.pubddl_table "
+ "FROM pg_publication p");
+ else if (fout->remoteVersion >= 130000)
appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, p.pubviaroot, false AS pubddl_database, false AS pubddl_table "
"FROM pg_publication p");
else if (fout->remoteVersion >= 110000)
appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, p.pubtruncate, false AS pubviaroot, false AS pubddl_database, false AS pubddl_table "
"FROM pg_publication p");
else
appendPQExpBuffer(query,
"SELECT p.tableoid, p.oid, p.pubname, "
"p.pubowner, "
- "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot "
+ "p.puballtables, p.pubinsert, p.pubupdate, p.pubdelete, false AS pubtruncate, false AS pubviaroot, false AS pubddl_database, false AS pubddl_table "
"FROM pg_publication p");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
@@ -3866,6 +3874,8 @@ getPublications(Archive *fout, int *numPublications)
i_pubdelete = PQfnumber(res, "pubdelete");
i_pubtruncate = PQfnumber(res, "pubtruncate");
i_pubviaroot = PQfnumber(res, "pubviaroot");
+ i_pubddl_database = PQfnumber(res, "pubddl_database");
+ i_pubddl_table = PQfnumber(res, "pubddl_table");
pubinfo = pg_malloc(ntups * sizeof(PublicationInfo));
@@ -3890,6 +3900,10 @@ getPublications(Archive *fout, int *numPublications)
(strcmp(PQgetvalue(res, i, i_pubtruncate), "t") == 0);
pubinfo[i].pubviaroot =
(strcmp(PQgetvalue(res, i, i_pubviaroot), "t") == 0);
+ pubinfo[i].pubddl_database =
+ (strcmp(PQgetvalue(res, i, i_pubddl_database), "t") == 0);
+ pubinfo[i].pubddl_table =
+ (strcmp(PQgetvalue(res, i, i_pubddl_table), "t") == 0);
/* Decide whether we want to dump it */
selectDumpableObject(&(pubinfo[i].dobj), fout);
@@ -3969,6 +3983,29 @@ dumpPublication(Archive *fout, const PublicationInfo *pubinfo)
appendPQExpBufferStr(query, "'");
+ appendPQExpBufferStr(query, ", ddl = '");
+ first = true;
+
+ if (pubinfo->pubddl_database)
+ {
+ if (!first)
+ appendPQExpBufferStr(query, ", ");
+
+ appendPQExpBufferStr(query, "database");
+ first = false;
+ }
+
+ if (pubinfo->pubddl_table)
+ {
+ if (!first)
+ appendPQExpBufferStr(query, ", ");
+
+ appendPQExpBufferStr(query, "table");
+ first = false;
+ }
+
+ appendPQExpBufferStr(query, "'");
+
if (pubinfo->pubviaroot)
appendPQExpBufferStr(query, ", publish_via_partition_root = true");
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 772dc0cf7a..17789ea271 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -620,6 +620,8 @@ typedef struct _PublicationInfo
bool pubdelete;
bool pubtruncate;
bool pubviaroot;
+ bool pubddl_database;
+ bool pubddl_table;
} PublicationInfo;
/*
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index fd1052e5db..bf86471d18 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2358,7 +2358,7 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub1;',
regexp => qr/^
- \QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate');\E
+ \QCREATE PUBLICATION pub1 WITH (publish = 'insert, update, delete, truncate', ddl = '');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
@@ -2367,9 +2367,9 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub2
FOR ALL TABLES
- WITH (publish = \'\');',
+ WITH (publish = \'\', ddl = \'\');',
regexp => qr/^
- \QCREATE PUBLICATION pub2 FOR ALL TABLES WITH (publish = '');\E
+ \QCREATE PUBLICATION pub2 FOR ALL TABLES WITH (publish = '', ddl = '');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
@@ -2378,7 +2378,7 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub3;',
regexp => qr/^
- \QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate');\E
+ \QCREATE PUBLICATION pub3 WITH (publish = 'insert, update, delete, truncate', ddl = '');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
@@ -2387,7 +2387,7 @@ my %tests = (
create_order => 50,
create_sql => 'CREATE PUBLICATION pub4;',
regexp => qr/^
- \QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate');\E
+ \QCREATE PUBLICATION pub4 WITH (publish = 'insert, update, delete, truncate', ddl = '');\E
/xm,
like => { %full_runs, section_post_data => 1, },
},
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 714097cad1..e609184303 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -5820,7 +5820,7 @@ listPublications(const char *pattern)
PQExpBufferData buf;
PGresult *res;
printQueryOpt myopt = pset.popt;
- static const bool translate_columns[] = {false, false, false, false, false, false, false, false};
+ static const bool translate_columns[] = {false, false, false, false, false, false, false, false, false, false};
if (pset.sversion < 100000)
{
@@ -5855,6 +5855,15 @@ listPublications(const char *pattern)
appendPQExpBuffer(&buf,
",\n pubviaroot AS \"%s\"",
gettext_noop("Via root"));
+ if (pset.sversion >= 140000)
+ {
+ appendPQExpBuffer(&buf,
+ ",\n pubddl_database AS \"%s\"",
+ gettext_noop("Database level DDLs"));
+ appendPQExpBuffer(&buf,
+ ",\n pubddl_table AS \"%s\"",
+ gettext_noop("Table level DDLs"));
+ }
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n");
@@ -5936,6 +5945,7 @@ describePublications(const char *pattern)
PGresult *res;
bool has_pubtruncate;
bool has_pubviaroot;
+ bool has_pubddl;
PQExpBufferData title;
printTableContent cont;
@@ -5952,6 +5962,7 @@ describePublications(const char *pattern)
has_pubtruncate = (pset.sversion >= 110000);
has_pubviaroot = (pset.sversion >= 130000);
+ has_pubddl = (pset.sversion >= 150000);
initPQExpBuffer(&buf);
@@ -5965,6 +5976,11 @@ describePublications(const char *pattern)
if (has_pubviaroot)
appendPQExpBufferStr(&buf,
", pubviaroot");
+ if (has_pubddl)
+ {
+ appendPQExpBufferStr(&buf,
+ ", pubddl_database, pubddl_table");
+ }
appendPQExpBufferStr(&buf,
"\nFROM pg_catalog.pg_publication\n");
@@ -6011,6 +6027,8 @@ describePublications(const char *pattern)
ncols++;
if (has_pubviaroot)
ncols++;
+ if (has_pubddl)
+ ncols += 2;
initPQExpBuffer(&title);
printfPQExpBuffer(&title, _("Publication %s"), pubname);
@@ -6025,6 +6043,11 @@ describePublications(const char *pattern)
printTableAddHeader(&cont, gettext_noop("Truncates"), true, align);
if (has_pubviaroot)
printTableAddHeader(&cont, gettext_noop("Via root"), true, align);
+ if (has_pubddl)
+ {
+ printTableAddHeader(&cont, gettext_noop("Database level DDL"), true, align);
+ printTableAddHeader(&cont, gettext_noop("Table level DDL"), true, align);
+ }
printTableAddCell(&cont, PQgetvalue(res, i, 2), false, false);
printTableAddCell(&cont, PQgetvalue(res, i, 3), false, false);
@@ -6035,6 +6058,11 @@ describePublications(const char *pattern)
printTableAddCell(&cont, PQgetvalue(res, i, 7), false, false);
if (has_pubviaroot)
printTableAddCell(&cont, PQgetvalue(res, i, 8), false, false);
+ if (has_pubddl)
+ {
+ printTableAddCell(&cont, PQgetvalue(res, i, 9), false, false);
+ printTableAddCell(&cont, PQgetvalue(res, i, 10), false, false);
+ }
if (!puballtables)
{
diff --git a/src/include/catalog/pg_publication.h b/src/include/catalog/pg_publication.h
index fe773cf9b7..b86b49aafc 100644
--- a/src/include/catalog/pg_publication.h
+++ b/src/include/catalog/pg_publication.h
@@ -20,6 +20,7 @@
#include "catalog/genbki.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_publication_d.h"
+#include "tcop/utility.h"
/* ----------------
* pg_publication definition. cpp turns this into
@@ -54,6 +55,12 @@ CATALOG(pg_publication,6104,PublicationRelationId)
/* true if partition changes are published using root schema */
bool pubviaroot;
+
+ /* true if database level ddls are published */
+ bool pubddl_database;
+
+ /* true if table level ddls are published */
+ bool pubddl_table;
} FormData_pg_publication;
/* ----------------
@@ -72,6 +79,8 @@ typedef struct PublicationActions
bool pubupdate;
bool pubdelete;
bool pubtruncate;
+ bool pubddl_database;
+ bool pubddl_table;
} PublicationActions;
typedef struct PublicationDesc
@@ -146,5 +155,6 @@ extern ObjectAddress publication_add_schema(Oid pubid, Oid schemaid,
extern Oid get_publication_oid(const char *pubname, bool missing_ok);
extern char *get_publication_name(Oid pubid, bool missing_ok);
+extern bool ddl_need_xlog(Oid relid, bool forAllTabPubOnly, bool isTopLevel);
#endif /* PG_PUBLICATION_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 56d2bb6616..073f46ef1a 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -15,6 +15,7 @@
#define DEFREM_H
#include "catalog/objectaddress.h"
+#include "catalog/pg_publication.h"
#include "nodes/params.h"
#include "parser/parse_node.h"
#include "tcop/dest.h"
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 4e191c120a..6761e3bbc0 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -30,20 +30,20 @@ ERROR: conflicting or redundant options
LINE 1: ...ub_xxx WITH (publish_via_partition_root = 'true', publish_vi...
^
\dRp
- List of publications
- Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f
- testpub_default | regress_publication_user | f | f | t | f | f | f
+ List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDLs | Table level DDLs
+--------------------+--------------------------+------------+---------+---------+---------+-----------+----------+---------------------+------------------
+ testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f | f | f
+ testpub_default | regress_publication_user | f | f | t | f | f | f | f | f
(2 rows)
ALTER PUBLICATION testpub_default SET (publish = 'insert, update, delete');
\dRp
- List of publications
- Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f
- testpub_default | regress_publication_user | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDLs | Table level DDLs
+--------------------+--------------------------+------------+---------+---------+---------+-----------+----------+---------------------+------------------
+ testpib_ins_trunct | regress_publication_user | f | t | f | f | f | f | f | f
+ testpub_default | regress_publication_user | f | t | t | t | f | f | f | f
(2 rows)
--- adding tables
@@ -87,10 +87,10 @@ RESET client_min_messages;
-- should be able to add schema to 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable ADD ALL TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"public.testpub_tbl1"
Tables from schemas:
@@ -99,20 +99,20 @@ Tables from schemas:
-- should be able to drop schema from 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable DROP ALL TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"public.testpub_tbl1"
-- should be able to set schema to 'FOR TABLE' publication
ALTER PUBLICATION testpub_fortable SET ALL TABLES IN SCHEMA pub_test;
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test"
@@ -134,10 +134,10 @@ ERROR: relation "testpub_nopk" is not part of the publication
-- should be able to set table to schema publication
ALTER PUBLICATION testpub_forschema SET TABLE pub_test.testpub_nopk;
\dRp+ testpub_forschema
- Publication testpub_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"pub_test.testpub_nopk"
@@ -159,10 +159,10 @@ Publications:
"testpub_foralltables"
\dRp+ testpub_foralltables
- Publication testpub_foralltables
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | t | t | t | f | f | f
+ Publication testpub_foralltables
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | t | t | t | f | f | f | t | t
(1 row)
DROP TABLE testpub_tbl2;
@@ -174,19 +174,19 @@ CREATE PUBLICATION testpub3 FOR TABLE testpub_tbl3;
CREATE PUBLICATION testpub4 FOR TABLE ONLY testpub_tbl3;
RESET client_min_messages;
\dRp+ testpub3
- Publication testpub3
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub3
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"public.testpub_tbl3"
"public.testpub_tbl3a"
\dRp+ testpub4
- Publication testpub4
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub4
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"public.testpub_tbl3"
@@ -207,10 +207,10 @@ UPDATE testpub_parted1 SET a = 1;
-- only parent is listed as being in publication, not the partition
ALTER PUBLICATION testpub_forparted ADD TABLE testpub_parted;
\dRp+ testpub_forparted
- Publication testpub_forparted
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_forparted
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"public.testpub_parted"
@@ -223,10 +223,10 @@ ALTER TABLE testpub_parted DETACH PARTITION testpub_parted1;
UPDATE testpub_parted1 SET a = 1;
ALTER PUBLICATION testpub_forparted SET (publish_via_partition_root = true);
\dRp+ testpub_forparted
- Publication testpub_forparted
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | t
+ Publication testpub_forparted
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | t | f | f
Tables:
"public.testpub_parted"
@@ -255,10 +255,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub5 FOR TABLE testpub_rf_tbl1, testpub_rf_tbl2 WHERE (c <> 'test' AND d < 5) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -271,10 +271,10 @@ Tables:
ALTER PUBLICATION testpub5 ADD TABLE testpub_rf_tbl3 WHERE (e > 1000 AND e < 2000);
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl2" WHERE ((c <> 'test'::text) AND (d < 5))
@@ -290,10 +290,10 @@ Publications:
ALTER PUBLICATION testpub5 DROP TABLE testpub_rf_tbl2;
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl3" WHERE ((e > 1000) AND (e < 2000))
@@ -301,10 +301,10 @@ Tables:
-- remove testpub_rf_tbl1 and add testpub_rf_tbl3 again (another WHERE expression)
ALTER PUBLICATION testpub5 SET TABLE testpub_rf_tbl3 WHERE (e > 300 AND e < 500);
\dRp+ testpub5
- Publication testpub5
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub5
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl3" WHERE ((e > 300) AND (e < 500))
@@ -337,10 +337,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_syntax1 FOR TABLE testpub_rf_tbl1, ONLY testpub_rf_tbl3 WHERE (e < 999) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub_syntax1
- Publication testpub_syntax1
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub_syntax1
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"public.testpub_rf_tbl3" WHERE (e < 999)
@@ -350,10 +350,10 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub_syntax2 FOR TABLE testpub_rf_tbl1, testpub_rf_schema1.testpub_rf_tbl5 WHERE (h < 999) WITH (publish = 'insert');
RESET client_min_messages;
\dRp+ testpub_syntax2
- Publication testpub_syntax2
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | f | f | f | f
+ Publication testpub_syntax2
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | f | f | f | f | f | f
Tables:
"public.testpub_rf_tbl1"
"testpub_rf_schema1.testpub_rf_tbl5" WHERE (h < 999)
@@ -658,10 +658,10 @@ ERROR: relation "testpub_tbl1" is already member of publication "testpub_fortbl
CREATE PUBLICATION testpub_fortbl FOR TABLE testpub_tbl1;
ERROR: publication "testpub_fortbl" already exists
\dRp+ testpub_fortbl
- Publication testpub_fortbl
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortbl
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"pub_test.testpub_nopk"
"public.testpub_tbl1"
@@ -699,10 +699,10 @@ Publications:
"testpub_fortbl"
\dRp+ testpub_default
- Publication testpub_default
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | f | f
+ Publication testpub_default
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | f | f | f | f
Tables:
"pub_test.testpub_nopk"
"public.testpub_tbl1"
@@ -780,10 +780,10 @@ REVOKE CREATE ON DATABASE regression FROM regress_publication_user2;
DROP TABLE testpub_parted;
DROP TABLE testpub_tbl1;
\dRp+ testpub_default
- Publication testpub_default
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | f | f
+ Publication testpub_default
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | f | f | f | f
(1 row)
-- fail - must be owner of publication
@@ -793,20 +793,20 @@ ERROR: must be owner of publication testpub_default
RESET ROLE;
ALTER PUBLICATION testpub_default RENAME TO testpub_foo;
\dRp testpub_foo
- List of publications
- Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
--------------+--------------------------+------------+---------+---------+---------+-----------+----------
- testpub_foo | regress_publication_user | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDLs | Table level DDLs
+-------------+--------------------------+------------+---------+---------+---------+-----------+----------+---------------------+------------------
+ testpub_foo | regress_publication_user | f | t | t | t | f | f | f | f
(1 row)
-- rename back to keep the rest simple
ALTER PUBLICATION testpub_foo RENAME TO testpub_default;
ALTER PUBLICATION testpub_default OWNER TO regress_publication_user2;
\dRp testpub_default
- List of publications
- Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
------------------+---------------------------+------------+---------+---------+---------+-----------+----------
- testpub_default | regress_publication_user2 | f | t | t | t | f | f
+ List of publications
+ Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDLs | Table level DDLs
+-----------------+---------------------------+------------+---------+---------+---------+-----------+----------+---------------------+------------------
+ testpub_default | regress_publication_user2 | f | t | t | t | f | f | f | f
(1 row)
-- adding schemas and tables
@@ -822,19 +822,19 @@ CREATE TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA"(id int);
SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub1_forschema FOR ALL TABLES IN SCHEMA pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
CREATE PUBLICATION testpub2_forschema FOR ALL TABLES IN SCHEMA pub_test1, pub_test2, pub_test3;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -848,44 +848,44 @@ CREATE PUBLICATION testpub6_forschema FOR ALL TABLES IN SCHEMA "CURRENT_SCHEMA",
CREATE PUBLICATION testpub_fortable FOR TABLE "CURRENT_SCHEMA"."CURRENT_SCHEMA";
RESET client_min_messages;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"public"
\dRp+ testpub4_forschema
- Publication testpub4_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub4_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"CURRENT_SCHEMA"
\dRp+ testpub5_forschema
- Publication testpub5_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub5_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"CURRENT_SCHEMA"
"public"
\dRp+ testpub6_forschema
- Publication testpub6_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub6_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"CURRENT_SCHEMA"
"public"
\dRp+ testpub_fortable
- Publication testpub_fortable
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortable
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"CURRENT_SCHEMA.CURRENT_SCHEMA"
@@ -919,10 +919,10 @@ ERROR: schema "testpub_view" does not exist
-- dropping the schema should reflect the change in publication
DROP SCHEMA pub_test3;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -930,20 +930,20 @@ Tables from schemas:
-- renaming the schema should reflect the change in publication
ALTER SCHEMA pub_test1 RENAME to pub_test1_renamed;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1_renamed"
"pub_test2"
ALTER SCHEMA pub_test1_renamed RENAME to pub_test1;
\dRp+ testpub2_forschema
- Publication testpub2_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub2_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -951,10 +951,10 @@ Tables from schemas:
-- alter publication add schema
ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -963,10 +963,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -975,10 +975,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema ADD ALL TABLES IN SCHEMA pub_test1;
ERROR: schema "pub_test1" is already member of publication "testpub1_forschema"
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -986,10 +986,10 @@ Tables from schemas:
-- alter publication drop schema
ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
@@ -997,10 +997,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test2;
ERROR: tables from schema "pub_test2" are not part of the publication
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
@@ -1008,29 +1008,29 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
-- drop all schemas
ALTER PUBLICATION testpub1_forschema DROP ALL TABLES IN SCHEMA pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
(1 row)
-- alter publication set multiple schema
ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA pub_test1, pub_test2;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1039,10 +1039,10 @@ Tables from schemas:
ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA non_existent_schema;
ERROR: schema "non_existent_schema" does not exist
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
"pub_test2"
@@ -1051,10 +1051,10 @@ Tables from schemas:
-- removing the duplicate schemas
ALTER PUBLICATION testpub1_forschema SET ALL TABLES IN SCHEMA pub_test1, pub_test1;
\dRp+ testpub1_forschema
- Publication testpub1_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub1_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
@@ -1124,18 +1124,18 @@ SET client_min_messages = 'ERROR';
CREATE PUBLICATION testpub3_forschema;
RESET client_min_messages;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
(1 row)
ALTER PUBLICATION testpub3_forschema SET ALL TABLES IN SCHEMA pub_test1;
\dRp+ testpub3_forschema
- Publication testpub3_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub3_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables from schemas:
"pub_test1"
@@ -1145,20 +1145,20 @@ CREATE PUBLICATION testpub_forschema_fortable FOR ALL TABLES IN SCHEMA pub_test1
CREATE PUBLICATION testpub_fortable_forschema FOR TABLE pub_test2.tbl1, ALL TABLES IN SCHEMA pub_test1;
RESET client_min_messages;
\dRp+ testpub_forschema_fortable
- Publication testpub_forschema_fortable
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_forschema_fortable
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"pub_test2.tbl1"
Tables from schemas:
"pub_test1"
\dRp+ testpub_fortable_forschema
- Publication testpub_fortable_forschema
- Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------------------------+------------+---------+---------+---------+-----------+----------
- regress_publication_user | f | t | t | t | t | f
+ Publication testpub_fortable_forschema
+ Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root | Database level DDL | Table level DDL
+--------------------------+------------+---------+---------+---------+-----------+----------+--------------------+-----------------
+ regress_publication_user | f | t | t | t | t | f | f | f
Tables:
"pub_test2.tbl1"
Tables from schemas:
--
2.25.1
--=-=-=
Content-Type: text/x-patch; charset=utf-8
Content-Disposition: attachment;
filename=v3-0002-Support-logical-logging-and-decoding-of-DDL-comma.patch
Content-Transfer-Encoding: quoted-printable
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:41 Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <tgl@sss.pgh.pa.us>
0 siblings, 1 reply; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:41 UTC (permalink / raw)
To: ; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Nikolay Shaplov <dhyan@nataraj.su>
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] 331+ messages in thread
* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
@ 2026-07-07 01:56 ` Tom Lane <tgl@sss.pgh.pa.us>
2026-07-07 05:44 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 1 reply; 331+ messages in thread
From: Tom Lane @ 2026-07-07 01:56 UTC (permalink / raw)
To: Andrey Rachitskiy <pl0h0yp1@gmail.com>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Nikolay Shaplov <dhyan@nataraj.su>
Andrey Rachitskiy <pl0h0yp1@gmail.com> 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] 331+ messages in thread
* Re: [PATCH] Limit PL/Perl scalar copies to work_mem
2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <tgl@sss.pgh.pa.us>
@ 2026-07-07 05:44 ` Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-07 05:44 UTC (permalink / raw)
To: Tom Lane <tgl@sss.pgh.pa.us>; +Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>; Nikolay Shaplov <dhyan@nataraj.su>
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 <tgl@sss.pgh.pa.us> wrote:
> Andrey Rachitskiy <pl0h0yp1@gmail.com> 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] 331+ messages in thread
end of thread, other threads:[~2026-07-07 05:44 UTC | newest]
Thread overview: 331+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2022-03-18 16:47 [PATCH v3 1/3] Define DDL replication levels via the CREATE PUBLICATION command, currently allow Database or Table level DDL replication. Zheng (Zane) Li <zhelli@amazon.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <tgl@sss.pgh.pa.us>
2026-07-07 05:44 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox