agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feed[PATCH v21 2/3] Lock referenced objects before permission checks in DDL commands
331+ messages / 3 participants
[nested] [flat]
* [PATCH v21 2/3] Lock referenced objects before permission checks in DDL commands
@ 2026-04-27 14:19 Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Bertrand Drouvot @ 2026-04-27 14:19 UTC (permalink / raw)
Add LockNotPinnedObjectById() calls before object_aclcheck() at all caller
sites where a permission check on a referenced object occurs before the
dependency on that object is recorded. This converts permission check before
lock to lock before permission check. It closes the TOCTOU window that could
allow a REVOKE to succeed between the permission check and the dependency
recording.
Author: Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
Reviewed-by:
Discussion: https://postgr.es/m/ZiYjn0eVc7pxVY45@ip-10-97-1-34.eu-west-3.compute.internal
---
src/backend/catalog/dependency.c | 22 ++++++++++++++++++++++
src/backend/catalog/namespace.c | 1 +
src/backend/catalog/pg_aggregate.c | 5 +++++
src/backend/catalog/pg_cast.c | 1 +
src/backend/catalog/pg_operator.c | 2 ++
src/backend/commands/aggregatecmds.c | 2 ++
src/backend/commands/alter.c | 3 +++
src/backend/commands/collationcmds.c | 2 ++
src/backend/commands/conversioncmds.c | 3 +++
src/backend/commands/extension.c | 1 +
src/backend/commands/foreigncmds.c | 5 +++++
src/backend/commands/functioncmds.c | 11 +++++++++++
src/backend/commands/indexcmds.c | 2 ++
src/backend/commands/opclasscmds.c | 2 ++
src/backend/commands/operatorcmds.c | 8 ++++++++
src/backend/commands/statscmds.c | 1 +
src/backend/commands/subscriptioncmds.c | 4 ++++
src/backend/commands/tablecmds.c | 7 +++++++
src/backend/commands/trigger.c | 2 ++
src/backend/commands/tsearchcmds.c | 2 ++
src/backend/commands/typecmds.c | 9 +++++++++
src/include/catalog/dependency.h | 1 +
22 files changed, 96 insertions(+)
22.5% src/backend/catalog/
76.2% src/backend/commands/
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 7e26691393d..a41e6ee2175 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1678,6 +1678,24 @@ LockNotPinnedObject(const ObjectAddress *object)
}
}
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object if it is not pinned. This is a convenience wrapper
+ * for callers that need to lock a referenced object before a permission
+ * check, converting permission check before lock to lock before permission
+ * check.
+ */
+void
+LockNotPinnedObjectById(Oid classid, Oid objid)
+{
+ ObjectAddress object;
+
+ ObjectAddressSet(object, classid, objid);
+
+ LockNotPinnedObject(&object);
+}
+
/*
* recordDependencyOnExpr - find expression dependencies
*
@@ -1960,8 +1978,11 @@ find_expr_references_walker(Node *node,
objoid = DatumGetObjectId(con->constvalue);
if (SearchSysCacheExists1(PROCOID,
ObjectIdGetDatum(objoid)))
+ {
+ LockNotPinnedObjectById(ProcedureRelationId, objoid);
add_object_address(ProcedureRelationId, objoid, 0,
context->addrs);
+ }
break;
case REGOPEROID:
case REGOPERATOROID:
@@ -2057,6 +2078,7 @@ find_expr_references_walker(Node *node,
{
FuncExpr *funcexpr = (FuncExpr *) node;
+ LockNotPinnedObjectById(ProcedureRelationId, funcexpr->funcid);
add_object_address(ProcedureRelationId, funcexpr->funcid, 0,
context->addrs);
/* fall through to examine arguments */
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 56b87d878e8..7dec076e111 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -3512,6 +3512,7 @@ LookupCreationNamespace(const char *nspname)
namespaceId = get_namespace_oid(nspname, false);
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 243b952b9cc..41b390e8d96 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -586,22 +586,26 @@ AggregateCreate(const char *aggName,
*/
for (i = 0; i < numArgs; i++)
{
+ LockNotPinnedObjectById(TypeRelationId, aggArgTypes[i]);
aclresult = object_aclcheck(TypeRelationId, aggArgTypes[i], GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggArgTypes[i]);
}
+ LockNotPinnedObjectById(TypeRelationId, aggTransType);
aclresult = object_aclcheck(TypeRelationId, aggTransType, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggTransType);
if (OidIsValid(aggmTransType))
{
+ LockNotPinnedObjectById(TypeRelationId, aggmTransType);
aclresult = object_aclcheck(TypeRelationId, aggmTransType, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, aggmTransType);
}
+ LockNotPinnedObjectById(TypeRelationId, finaltype);
aclresult = object_aclcheck(TypeRelationId, finaltype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, finaltype);
@@ -909,6 +913,7 @@ lookup_agg_function(List *fnName,
}
/* Check aggregate creator has permission to call the function */
+ LockNotPinnedObjectById(ProcedureRelationId, fnOid);
aclresult = object_aclcheck(ProcedureRelationId, fnOid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(fnOid));
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 5119c2acda2..bb48e686f83 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -105,6 +105,7 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
/* dependency on function */
if (OidIsValid(funcid))
{
+ LockNotPinnedObjectById(ProcedureRelationId, funcid);
ObjectAddressSet(referenced, ProcedureRelationId, funcid);
add_exact_object_address(&referenced, addrs);
}
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 6b90c774c18..a222358d081 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -654,6 +654,7 @@ get_other_operator(List *otherOp, Oid otherLeftTypeId, Oid otherRightTypeId,
/* not in catalogs, different from operator, so make shell */
+ LockNotPinnedObjectById(NamespaceRelationId, otherNamespace);
aclresult = object_aclcheck(NamespaceRelationId, otherNamespace, GetUserId(),
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -876,6 +877,7 @@ makeOperatorDependencies(HeapTuple tuple,
/* Dependency on namespace */
if (OidIsValid(oper->oprnamespace))
{
+ LockNotPinnedObjectById(NamespaceRelationId, oper->oprnamespace);
ObjectAddressSet(referenced, NamespaceRelationId, oper->oprnamespace);
add_exact_object_address(&referenced, addrs);
}
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 41b45dc6402..c2fb111daf3 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -22,6 +22,7 @@
*/
#include "postgres.h"
+#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_namespace.h"
@@ -101,6 +102,7 @@ DefineAggregate(ParseState *pstate,
aggNamespace = QualifiedNameGetCreationNamespace(name, &aggName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, aggNamespace);
aclresult = object_aclcheck(NamespaceRelationId, aggNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 74ceb5fe20d..03516cb0d2e 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -221,6 +221,7 @@ AlterObjectRename_internal(Relation rel, Oid objectId, const char *new_name)
/* User must have CREATE privilege on the namespace */
if (OidIsValid(namespaceId))
{
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -752,6 +753,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
NameStr(*(DatumGetName(name))));
/* User must have CREATE privilege on new namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, nspOid);
aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -1014,6 +1016,7 @@ AlterObjectOwner_internal(Oid classId, Oid objectId, Oid new_ownerId)
{
AclResult aclresult;
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, new_ownerId,
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c
index 0bc31ec2b6f..fa5dad1aa1f 100644
--- a/src/backend/commands/collationcmds.c
+++ b/src/backend/commands/collationcmds.c
@@ -21,6 +21,7 @@
#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
@@ -82,6 +83,7 @@ DefineCollation(ParseState *pstate, List *names, List *parameters, bool if_not_e
collNamespace = QualifiedNameGetCreationNamespace(names, &collName);
+ LockNotPinnedObjectById(NamespaceRelationId, collNamespace);
aclresult = object_aclcheck(NamespaceRelationId, collNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 5f2022d3072..d60ecdb711a 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "catalog/dependency.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
@@ -50,6 +51,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
&conversion_name);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -97,6 +99,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
NameListToString(func_name), "integer")));
/* Check we have EXECUTE rights for the function */
+ LockNotPinnedObjectById(ProcedureRelationId, funcoid);
aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION,
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index a330b5fd6ce..4e82a010788 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -3283,6 +3283,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
extensionName);
/* Permission check: must have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, nspOid);
aclresult = object_aclcheck(NamespaceRelationId, nspOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA, newschema);
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index c4852be2eb2..dd31e260739 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -377,6 +377,7 @@ AlterForeignServerOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
check_can_set_role(GetUserId(), newOwnerId);
/* New owner must have USAGE privilege on foreign-data wrapper */
+ LockNotPinnedObjectById(ForeignDataWrapperRelationId, form->srvfdw);
aclresult = object_aclcheck(ForeignDataWrapperRelationId, form->srvfdw, newOwnerId, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
{
@@ -997,6 +998,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
*/
fdw = GetForeignDataWrapperByName(stmt->fdwname, false);
+ LockNotPinnedObjectById(ForeignDataWrapperRelationId, fdw->fdwid);
aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdw->fdwid, ownerId, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FDW, fdw->fdwname);
@@ -1188,6 +1190,7 @@ user_mapping_ddl_aclcheck(Oid umuserid, Oid serverid, const char *servername)
{
AclResult aclresult;
+ LockNotPinnedObjectById(ForeignServerRelationId, serverid);
aclresult = object_aclcheck(ForeignServerRelationId, serverid, curuserid, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, servername);
@@ -1539,6 +1542,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
* get the actual FDW for option validation etc.
*/
server = GetForeignServerByName(stmt->servername, false);
+ LockNotPinnedObjectById(ForeignServerRelationId, server->serverid);
aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, ownerId, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
@@ -1598,6 +1602,7 @@ ImportForeignSchema(ImportForeignSchemaStmt *stmt)
/* Check that the foreign server exists and that we have USAGE on it */
server = GetForeignServerByName(stmt->server_name, false);
+ LockNotPinnedObjectById(ForeignServerRelationId, server->serverid);
aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 3afd762e9dc..9aec534c390 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -146,6 +146,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
errdetail("Creating a shell type definition.")));
namespaceId = QualifiedNameGetCreationNamespace(returnType->names,
&typname);
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -158,6 +159,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
CommandCounterIncrement();
}
+ LockNotPinnedObjectById(TypeRelationId, rettype);
aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, rettype);
@@ -274,6 +276,7 @@ interpret_function_parameter_list(ParseState *pstate,
toid = InvalidOid; /* keep compiler quiet */
}
+ LockNotPinnedObjectById(TypeRelationId, toid);
aclresult = object_aclcheck(TypeRelationId, toid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, toid);
@@ -1071,6 +1074,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
&funcname);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -1125,6 +1129,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
if (languageStruct->lanpltrusted)
{
/* if trusted language, need USAGE privilege */
+ LockNotPinnedObjectById(LanguageRelationId, languageOid);
aclresult = object_aclcheck(LanguageRelationId, languageOid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_LANGUAGE,
@@ -1582,10 +1587,12 @@ CreateCast(CreateCastStmt *stmt)
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
+ LockNotPinnedObjectById(TypeRelationId, sourcetypeid);
aclresult = object_aclcheck(TypeRelationId, sourcetypeid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, sourcetypeid);
+ LockNotPinnedObjectById(TypeRelationId, targettypeid);
aclresult = object_aclcheck(TypeRelationId, targettypeid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, targettypeid);
@@ -1874,6 +1881,7 @@ CreateTransform(CreateTransformStmt *stmt)
if (!object_ownercheck(TypeRelationId, typeid, GetUserId()))
aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid);
+ LockNotPinnedObjectById(TypeRelationId, typeid);
aclresult = object_aclcheck(TypeRelationId, typeid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, typeid);
@@ -1883,6 +1891,7 @@ CreateTransform(CreateTransformStmt *stmt)
*/
langid = get_language_oid(stmt->lang, false);
+ LockNotPinnedObjectById(LanguageRelationId, langid);
aclresult = object_aclcheck(LanguageRelationId, langid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_LANGUAGE, stmt->lang);
@@ -1897,6 +1906,7 @@ CreateTransform(CreateTransformStmt *stmt)
if (!object_ownercheck(ProcedureRelationId, fromsqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
+ LockNotPinnedObjectById(ProcedureRelationId, fromsqlfuncid);
aclresult = object_aclcheck(ProcedureRelationId, fromsqlfuncid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->fromsql->objname));
@@ -1923,6 +1933,7 @@ CreateTransform(CreateTransformStmt *stmt)
if (!object_ownercheck(ProcedureRelationId, tosqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
+ LockNotPinnedObjectById(ProcedureRelationId, tosqlfuncid);
aclresult = object_aclcheck(ProcedureRelationId, tosqlfuncid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 9ab74c8df0a..6216ab2a5f5 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -25,6 +25,7 @@
#include "access/tableam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
@@ -770,6 +771,7 @@ DefineIndex(ParseState *pstate,
{
AclResult aclresult;
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, root_save_userid,
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 7493a9ccc06..ad71ee53cb2 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -363,6 +363,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
&opcname);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceoid);
aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -801,6 +802,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
&opfname);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceoid);
aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 3e7b09b3494..bb2ce833bdd 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -33,6 +33,7 @@
#include "access/htup_details.h"
#include "access/table.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
@@ -92,6 +93,7 @@ DefineOperator(List *names, List *parameters)
oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, oprNamespace);
aclresult = object_aclcheck(NamespaceRelationId, oprNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -189,6 +191,7 @@ DefineOperator(List *names, List *parameters)
if (typeName1)
{
+ LockNotPinnedObjectById(TypeRelationId, typeId1);
aclresult = object_aclcheck(TypeRelationId, typeId1, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, typeId1);
@@ -196,6 +199,7 @@ DefineOperator(List *names, List *parameters)
if (typeName2)
{
+ LockNotPinnedObjectById(TypeRelationId, typeId2);
aclresult = object_aclcheck(TypeRelationId, typeId2, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, typeId2);
@@ -227,12 +231,14 @@ DefineOperator(List *names, List *parameters)
* necessary, since EXECUTE will be checked at any attempted use of the
* operator, but it seems like a good idea anyway.
*/
+ LockNotPinnedObjectById(ProcedureRelationId, functionOid);
aclresult = object_aclcheck(ProcedureRelationId, functionOid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION,
NameListToString(functionName));
rettype = get_func_rettype(functionOid);
+ LockNotPinnedObjectById(TypeRelationId, rettype);
aclresult = object_aclcheck(TypeRelationId, rettype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, rettype);
@@ -312,6 +318,7 @@ ValidateRestrictionEstimator(List *restrictionName)
{
AclResult aclresult;
+ LockNotPinnedObjectById(ProcedureRelationId, restrictionOid);
aclresult = object_aclcheck(ProcedureRelationId, restrictionOid,
GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
@@ -382,6 +389,7 @@ ValidateJoinEstimator(List *joinName)
{
AclResult aclresult;
+ LockNotPinnedObjectById(ProcedureRelationId, joinOid);
aclresult = object_aclcheck(ProcedureRelationId, joinOid,
GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index b354723be44..6afef0f55c4 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -185,6 +185,7 @@ CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
{
AclResult aclresult;
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceId);
aclresult = object_aclcheck(NamespaceRelationId, namespaceId,
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/subscriptioncmds.c b/src/backend/commands/subscriptioncmds.c
index 523959ba0ce..08dea4b67b3 100644
--- a/src/backend/commands/subscriptioncmds.c
+++ b/src/backend/commands/subscriptioncmds.c
@@ -745,6 +745,7 @@ CreateSubscription(ParseState *pstate, CreateSubscriptionStmt *stmt,
conninfo = NULL;
server = GetForeignServerByName(stmt->servername, false);
+ LockNotPinnedObjectById(ForeignServerRelationId, server->serverid);
aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, owner, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FOREIGN_SERVER, server->servername);
@@ -1833,6 +1834,7 @@ AlterSubscription(ParseState *pstate, AlterSubscriptionStmt *stmt,
* the server.
*/
new_server = GetForeignServerByName(stmt->servername, false);
+ LockNotPinnedObjectById(ForeignServerRelationId, new_server->serverid);
aclresult = object_aclcheck(ForeignServerRelationId,
new_server->serverid,
form->subowner, ACL_USAGE);
@@ -2262,6 +2264,7 @@ DropSubscription(DropSubscriptionStmt *stmt, bool isTopLevel)
ForeignServer *server;
server = GetForeignServer(form->subserver);
+ LockNotPinnedObjectById(ForeignServerRelationId, form->subserver);
aclresult = object_aclcheck(ForeignServerRelationId, form->subserver,
form->subowner, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
@@ -2606,6 +2609,7 @@ AlterSubscriptionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
ForeignServer *server = GetForeignServer(form->subserver);
+ LockNotPinnedObjectById(ForeignServerRelationId, server->serverid);
aclresult = object_aclcheck(ForeignServerRelationId, server->serverid, newOwnerId, ACL_USAGE);
if (aclresult != ACLCHECK_OK)
ereport(ERROR,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 92b0f38c353..d95ef23a53f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -30,6 +30,7 @@
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
@@ -997,6 +998,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
+ LockNotPinnedObjectById(TypeRelationId, ofTypeId);
aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, ofTypeId);
@@ -1462,6 +1464,7 @@ BuildDescForRelation(const List *columns)
attname = entry->colname;
typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
+ LockNotPinnedObjectById(TypeRelationId, atttypid);
aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, atttypid);
@@ -13941,6 +13944,7 @@ checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
int i;
/* Okay if we have relation-level REFERENCES permission */
+ LockNotPinnedObjectById(RelationRelationId, RelationGetRelid(rel));
aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
ACL_REFERENCES);
if (aclresult == ACLCHECK_OK)
@@ -14724,6 +14728,7 @@ ATPrepAlterColumnType(List **wqueue,
/* Look up the target type */
typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
+ LockNotPinnedObjectById(TypeRelationId, targettype);
aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, targettype);
@@ -16476,6 +16481,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
check_can_set_role(GetUserId(), newOwnerId);
/* New owner must have CREATE privilege on namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceOid);
aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -19882,6 +19888,7 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
*/
if (IsA(stmt, RenameStmt))
{
+ LockNotPinnedObjectById(NamespaceRelationId, classform->relnamespace);
aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index b87b4b40d07..3c5ca8fe7e3 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -350,6 +350,7 @@ CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString,
if (OidIsValid(constrrelid))
{
+ LockNotPinnedObjectById(RelationRelationId, constrrelid);
aclresult = pg_class_aclcheck(constrrelid, GetUserId(),
ACL_TRIGGER);
if (aclresult != ACLCHECK_OK)
@@ -695,6 +696,7 @@ CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString,
funcoid = LookupFuncName(stmt->funcname, 0, NULL, false);
if (!isInternal)
{
+ LockNotPinnedObjectById(ProcedureRelationId, funcoid);
aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION,
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index a12ce33bae4..eb9e68bbb91 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -414,6 +414,7 @@ DefineTSDictionary(List *names, List *parameters)
namespaceoid = QualifiedNameGetCreationNamespace(names, &dictname);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceoid);
aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -917,6 +918,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
namespaceoid = QualifiedNameGetCreationNamespace(names, &cfgname);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, namespaceoid);
aclresult = object_aclcheck(NamespaceRelationId, namespaceoid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c4c3cdb5461..0e01dda90ba 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -39,6 +39,7 @@
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
@@ -739,6 +740,7 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
&domainName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, domainNamespace);
aclresult = object_aclcheck(NamespaceRelationId, domainNamespace, GetUserId(),
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -788,6 +790,7 @@ DefineDomain(ParseState *pstate, CreateDomainStmt *stmt)
TypeNameToString(stmt->typeName)),
parser_errposition(pstate, stmt->typeName->location)));
+ LockNotPinnedObjectById(TypeRelationId, basetypeoid);
aclresult = object_aclcheck(TypeRelationId, basetypeoid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error_type(aclresult, basetypeoid);
@@ -1195,6 +1198,7 @@ DefineEnum(CreateEnumStmt *stmt)
&enumName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, enumNamespace);
aclresult = object_aclcheck(NamespaceRelationId, enumNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -1420,6 +1424,7 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
&typeName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, typeNamespace);
aclresult = object_aclcheck(NamespaceRelationId, typeNamespace, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
@@ -1496,6 +1501,7 @@ DefineRange(ParseState *pstate, CreateRangeStmt *stmt)
&multirangeTypeName);
/* Check we have creation rights in target namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, multirangeNamespace);
aclresult = object_aclcheck(NamespaceRelationId, multirangeNamespace,
GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
@@ -2421,6 +2427,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid)
func_signature_string(procname, 1, NIL, argList))));
/* Also, range type's creator must have permission to call function */
+ LockNotPinnedObjectById(ProcedureRelationId, procOid);
aclresult = object_aclcheck(ProcedureRelationId, procOid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
@@ -2464,6 +2471,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype)
func_signature_string(procname, 2, NIL, argList))));
/* Also, range type's creator must have permission to call function */
+ LockNotPinnedObjectById(ProcedureRelationId, procOid);
aclresult = object_aclcheck(ProcedureRelationId, procOid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(procOid));
@@ -3963,6 +3971,7 @@ AlterTypeOwner(List *names, Oid newOwnerId, ObjectType objecttype)
check_can_set_role(GetUserId(), newOwnerId);
/* New owner must have CREATE privilege on namespace */
+ LockNotPinnedObjectById(NamespaceRelationId, typTup->typnamespace);
aclresult = object_aclcheck(NamespaceRelationId, typTup->typnamespace,
newOwnerId,
ACL_CREATE);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index fa508ea70c6..ac1d902e912 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -102,6 +102,7 @@ typedef struct ObjectAddresses ObjectAddresses;
extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
extern void LockNotPinnedObject(const ObjectAddress *object);
+extern void LockNotPinnedObjectById(Oid classid, Oid objid);
extern bool isObjectPinned(const ObjectAddress *object);
extern void ReleaseDeletionLock(const ObjectAddress *object);
--
2.34.1
--FjYZKkuBdH+F20sI
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="v21-0003-Add-Assert-guard-to-detect-permission-check-befo.patch"
Content-Transfer-Encoding: 8bit
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+gkG/PMs8Alw6m8e--
^ permalink raw reply [nested|flat] 331+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem
@ 2026-07-06 22:13 Andrey Rachitskiy <pl0h0yp1@gmail.com>
0 siblings, 0 replies; 331+ messages in thread
From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw)
When a PL/Perl function returns a very large text value, sv2cstr() copies
the entire Perl string into backend memory with no size check. A user
with permission to create untrusted PL/Perl functions can return strings
far larger than work_mem and risk getting the backend killed by the OOM
killer.
Reject Perl strings larger than work_mem * 1024 bytes, capped by
MaxAllocSize, before copying them through sv2cstr(). This follows the
same work_mem-based limit pattern used elsewhere in the backend.
Add a plperl regression test that attempts to return a 16MB string with
the default 4MB work_mem setting.
Author: Andrey Rachitskiy <pl0h0yp1@gmail.com>
---
src/pl/plperl/expected/plperl.out | 8 +++++++
src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++
src/pl/plperl/sql/plperl.sql | 7 ++++++
3 files changed, 49 insertions(+)
diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index e3d7c88..50c788b 100644
--- a/src/pl/plperl/expected/plperl.out
+++ b/src/pl/plperl/expected/plperl.out
@@ -792,3 +792,11 @@ SELECT self_modify(42);
126
(1 row)
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+SELECT perl_oversized_text();
+ERROR: Perl value exceeds maximum allowed size (4194304 bytes)
+HINT: Increase work_mem or reduce the result size.
+CONTEXT: PL/Perl function "perl_oversized_text"
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 4c03f9e..1ccce6d 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -17,6 +17,8 @@
/* defines free() by way of system headers, so must be included before perl.h */
#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/memutils.h"
/*
* Pull in Perl headers via a wrapper header, to control the scope of
@@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *);
void plperl_util_elog(int level, SV *msg);
+/*
+ * Maximum byte size for a Perl scalar copied through sv2cstr().
+ *
+ * This follows the same work_mem * 1024 pattern used elsewhere in the
+ * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize.
+ */
+static inline Size
+plperl_max_scalar_bytes(void)
+{
+ Size limit = (Size) work_mem * (Size) 1024;
+
+ return Min(limit, MaxAllocSize - 1);
+}
+
+/*
+ * Reject Perl strings that are too large to copy into backend memory.
+ */
+static inline void
+plperl_check_sv_length(STRLEN len)
+{
+ Size max_len = plperl_max_scalar_bytes();
+
+ if ((Size) len > max_len)
+ erereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("Perl value exceeds maximum allowed size (%zu bytes)",
+ max_len),
+ errhint("Increase work_mem or reduce the result size.")));
+}
+
/* helper functions */
/*
@@ -127,6 +159,8 @@ sv2cstr(SV *sv)
else
val = SvPVutf8(sv, len);
+ plperl_check_sv_length(len);
+
/*
* Now convert to database encoding. We use perl's length in the event we
* had an embedded null byte to ensure we error out properly.
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index bb0b8ce..0470a2b 100644
--- a/src/pl/plperl/sql/plperl.sql
+++ b/src/pl/plperl/sql/plperl.sql
@@ -521,3 +521,10 @@ $$ LANGUAGE plperl;
SELECT self_modify(42);
SELECT self_modify(42);
+
+-- oversized text results are rejected at the PL boundary
+CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$
+ return 'x' x (16 * 1024 * 1024);
+$$ LANGUAGE plperl;
+
+SELECT perl_oversized_text();
--MP_/sDTAhto+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>
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-07 01:56 Tom Lane <tgl@sss.pgh.pa.us>
parent: 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-07 05:44 Andrey Rachitskiy <pl0h0yp1@gmail.com>
parent: Tom Lane <tgl@sss.pgh.pa.us>
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 --
2026-04-27 14:19 [PATCH v21 2/3] Lock referenced objects before permission checks in DDL commands Bertrand Drouvot <bertranddrouvot.pg@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.com>
2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <pl0h0yp1@gmail.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