agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v5 5/7] Row pattern recognition patch (docs). 244+ messages / 3 participants [nested] [flat]
* [PATCH v5 5/7] Row pattern recognition patch (docs). @ 2023-09-02 06:32 Tatsuo Ishii <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Tatsuo Ishii @ 2023-09-02 06:32 UTC (permalink / raw) --- doc/src/sgml/advanced.sgml | 52 ++++++++++++++++++++++++++++++++++ doc/src/sgml/func.sgml | 54 ++++++++++++++++++++++++++++++++++++ doc/src/sgml/ref/select.sgml | 38 +++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/advanced.sgml b/doc/src/sgml/advanced.sgml index 755c9f1485..eda3612822 100644 --- a/doc/src/sgml/advanced.sgml +++ b/doc/src/sgml/advanced.sgml @@ -537,6 +537,58 @@ WHERE pos < 3; <literal>rank</literal> less than 3. </para> + <para> + Row pattern common syntax can be used with row pattern common syntax to + perform row pattern recognition in a query. Row pattern common syntax + includes two sub clauses. <literal>DEFINE</literal> defines definition + variables along with an expression. The expression must be a logical + expression, which means it must + return <literal>TRUE</literal>, <literal>FALSE</literal> + or <literal>NULL</literal>. Moreover if the expression comprises a column + reference, it must be the argument of <function>rpr</function>. An example + of <literal>DEFINE</literal> is as follows. + +<programlisting> +DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +</programlisting> + + Note that <function>PREV</function> returns price column in the previous + row if it's called in a context of row pattern recognition. So in the + second line means the definition variable "UP" is <literal>TRUE</literal> + when price column in the current row is greater than the price column in + the previous row. Likewise, "DOWN" is <literal>TRUE</literal> when when + price column in the current row is lower than the price column in the + previous row. + </para> + <para> + Once <literal>DEFINE</literal> exists, <literal>PATTERN</literal> can be + used. <literal>PATTERN</literal> defines a sequence of rows that satisfies + certain conditions. For example following <literal>PATTERN</literal> + defines that a row starts with the condition "LOWPRICE", then one or more + rows satisfy "UP" and finally one or more rows satisfy "DOWN". If a + sequence of rows found, rpr returns the column at the starting row. + Example of a <literal>SELECT</literal> using the <literal>DEFINE</literal> + and <literal>PATTERN</literal> clause is as follows. + +<programlisting> +SELECT company, tdate, price, max(price) OVER w FROM stock + WINDOW w AS ( + PARTITION BY company + ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING + AFTER MATCH SKIP PAST LAST ROW + INITIAL + PATTERN (LOWPRICE UP+ DOWN+) + DEFINE + LOWPRICE AS price <= 100, + UP AS price > PREV(price), + DOWN AS price < PREV(price) +); +</programlisting> + </para> + <para> When a query involves multiple window functions, it is possible to write out each one with a separate <literal>OVER</literal> clause, but this is diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7a0d4b9134..b7bfc9271e 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -21772,6 +21772,7 @@ SELECT count(*) FROM sometable; returns <literal>NULL</literal> if there is no such row. </para></entry> </row> + </tbody> </tgroup> </table> @@ -21811,6 +21812,59 @@ SELECT count(*) FROM sometable; Other frame specifications can be used to obtain other effects. </para> + <para> + Row pattern recognition navigation functions are listed in + <xref linkend="functions-rpr-navigation-table"/>. These functions + can be used to describe DEFINE clause of Row pattern recognition. + </para> + + <table id="functions-rpr-navigation-table"> + <title>Row Pattern Navigation Functions</title> + <tgroup cols="1"> + <thead> + <row> + <entry role="func_table_entry"><para role="func_signature"> + Function + </para> + <para> + Description + </para></entry> + </row> + </thead> + + <tbody> + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>prev</primary> + </indexterm> + <function>prev</function> ( <parameter>value</parameter> <type>anyelement</type> ) + <returnvalue>anyelement</returnvalue> + </para> + <para> + Returns the column value at the previous row; + returns NULL if there is no previous row in the window frame. + </para></entry> + </row> + + <row> + <entry role="func_table_entry"><para role="func_signature"> + <indexterm> + <primary>next</primary> + </indexterm> + <function>next</function> ( <parameter>value</parameter> <type>anyelement</type> ) + <returnvalue>anyelement</returnvalue> + </para> + <para> + Returns the column value at the next row; + returns NULL if there is no next row in the window frame. + </para></entry> + </row> + + </tbody> + </tgroup> + </table> + <note> <para> The SQL standard defines a <literal>RESPECT NULLS</literal> or diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml index 0ee0cc7e64..8d3becd57a 100644 --- a/doc/src/sgml/ref/select.sgml +++ b/doc/src/sgml/ref/select.sgml @@ -966,8 +966,8 @@ WINDOW <replaceable class="parameter">window_name</replaceable> AS ( <replaceabl The <replaceable class="parameter">frame_clause</replaceable> can be one of <synopsis> -{ RANGE | ROWS | GROUPS } <replaceable>frame_start</replaceable> [ <replaceable>frame_exclusion</replaceable> ] -{ RANGE | ROWS | GROUPS } BETWEEN <replaceable>frame_start</replaceable> AND <replaceable>frame_end</replaceable> [ <replaceable>frame_exclusion</replaceable> ] +{ RANGE | ROWS | GROUPS } <replaceable>frame_start</replaceable> [ <replaceable>frame_exclusion</replaceable> ] [row_pattern_common_syntax] +{ RANGE | ROWS | GROUPS } BETWEEN <replaceable>frame_start</replaceable> AND <replaceable>frame_end</replaceable> [ <replaceable>frame_exclusion</replaceable> ] [row_pattern_common_syntax] </synopsis> where <replaceable>frame_start</replaceable> @@ -1074,6 +1074,40 @@ EXCLUDE NO OTHERS a given peer group will be in the frame or excluded from it. </para> + <para> + The + optional <replaceable class="parameter">row_pattern_common_syntax</replaceable> + defines the <firstterm>row pattern recognition condition</firstterm> for + this + window. <replaceable class="parameter">row_pattern_common_syntax</replaceable> + includes following subclauses. <literal>AFTER MATCH SKIP PAST LAST + ROW</literal> or <literal>AFTER MATCH SKIP TO NEXT ROW</literal> controls + how to proceed to next row position after a match + found. With <literal>AFTER MATCH SKIP PAST LAST ROW</literal> (the + default) next row position is next to the last row of previous match. On + the other hand, with <literal>AFTER MATCH SKIP TO NEXT ROW</literal> next + row position is always next to the last row of previous + match. <literal>DEFINE</literal> defines definition variables along with a + boolean expression. <literal>PATTERN</literal> defines a sequence of rows + that satisfies certain conditions using variables defined + in <literal>DEFINE</literal> clause. If the variable is not defined in + the <literal>DEFINE</literal> clause, it is implicitly assumed + following is defined in the <literal>DEFINE</literal> clause. + +<synopsis> +<literal>variable_name</literal> AS TRUE +</synopsis> + + Note that the maximu number of variables defined + in <literal>DEFINE</literal> clause is 26. + +<synopsis> +[ AFTER MATCH SKIP PAST LAST ROW | AFTER MATCH SKIP TO NEXT ROW ] +PATTERN <replaceable class="parameter">pattern_variable_name</replaceable>[+] [, ...] +DEFINE <replaceable class="parameter">definition_varible_name</replaceable> AS <replaceable class="parameter">expression</replaceable> [, ...] +</synopsis> + </para> + <para> The purpose of a <literal>WINDOW</literal> clause is to specify the behavior of <firstterm>window functions</firstterm> appearing in the query's -- 2.25.1 ----Next_Part(Sat_Sep__2_15_52_35_2023_273)-- Content-Type: Text/X-Patch; charset=us-ascii Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="v5-0006-Row-pattern-recognition-patch-tests.patch" ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:13 Andrey Rachitskiy <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:13 UTC (permalink / raw) When a PL/Perl function returns a very large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. A user with permission to create untrusted PL/Perl functions can return strings far larger than work_mem and risk getting the backend killed by the OOM killer. Reject Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). This follows the same work_mem-based limit pattern used elsewhere in the backend. Add a plperl regression test that attempts to return a 16MB string with the default 4MB work_mem setting. Author: Andrey Rachitskiy <[email protected]> --- src/pl/plperl/expected/plperl.out | 8 +++++++ src/pl/plperl/plperl.h | 34 ++++++++++++++++++++++++++++++ src/pl/plperl/sql/plperl.sql | 7 ++++++ 3 files changed, 49 insertions(+) diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out index e3d7c88..50c788b 100644 --- a/src/pl/plperl/expected/plperl.out +++ b/src/pl/plperl/expected/plperl.out @@ -792,3 +792,11 @@ SELECT self_modify(42); 126 (1 row) +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; +SELECT perl_oversized_text(); +ERROR: Perl value exceeds maximum allowed size (4194304 bytes) +HINT: Increase work_mem or reduce the result size. +CONTEXT: PL/Perl function "perl_oversized_text" diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h index 4c03f9e..1ccce6d 100644 --- a/src/pl/plperl/plperl.h +++ b/src/pl/plperl/plperl.h @@ -17,6 +17,8 @@ /* defines free() by way of system headers, so must be included before perl.h */ #include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/memutils.h" /* * Pull in Perl headers via a wrapper header, to control the scope of @@ -40,6 +42,36 @@ char *plperl_sv_to_literal(SV *, char *); void plperl_util_elog(int level, SV *msg); +/* + * Maximum byte size for a Perl scalar copied through sv2cstr(). + * + * This follows the same work_mem * 1024 pattern used elsewhere in the + * backend (e.g. reorderbuffer.c, nodeHash.c) and is capped by MaxAllocSize. + */ +static inline Size +plperl_max_scalar_bytes(void) +{ + Size limit = (Size) work_mem * (Size) 1024; + + return Min(limit, MaxAllocSize - 1); +} + +/* + * Reject Perl strings that are too large to copy into backend memory. + */ +static inline void +plperl_check_sv_length(STRLEN len) +{ + Size max_len = plperl_max_scalar_bytes(); + + if ((Size) len > max_len) + erereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("Perl value exceeds maximum allowed size (%zu bytes)", + max_len), + errhint("Increase work_mem or reduce the result size."))); +} + /* helper functions */ /* @@ -127,6 +159,8 @@ sv2cstr(SV *sv) else val = SvPVutf8(sv, len); + plperl_check_sv_length(len); + /* * Now convert to database encoding. We use perl's length in the event we * had an embedded null byte to ensure we error out properly. diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql index bb0b8ce..0470a2b 100644 --- a/src/pl/plperl/sql/plperl.sql +++ b/src/pl/plperl/sql/plperl.sql @@ -521,3 +521,10 @@ $$ LANGUAGE plperl; SELECT self_modify(42); SELECT self_modify(42); + +-- oversized text results are rejected at the PL boundary +CREATE OR REPLACE FUNCTION perl_oversized_text() RETURNS text AS $$ + return 'x' x (16 * 1024 * 1024); +$$ LANGUAGE plperl; + +SELECT perl_oversized_text(); --MP_/sDTAhto+gkG/PMs8Alw6m8e-- ^ permalink raw reply [nested|flat] 244+ messages in thread
* [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-06 22:41 Andrey Rachitskiy <[email protected]> 0 siblings, 1 reply; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-06 22:41 UTC (permalink / raw) To: ; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]> Hi, Hackers! When a PL/Perl function returns a large text value, sv2cstr() copies the entire Perl string into backend memory with no size check. The helper is used on the path from Perl return values and SPI arguments to PostgreSQL text datums; it simply palloc()s a copy after SvPVutf8(). A user who is allowed to create untrusted PL/Perl functions can therefore force the backend to allocate strings far larger than any session limit. On a memory-constrained host this can get the backend process killed by the OOM killer (SIGKILL) rather than raising a catchable PostgreSQL error. Reproducer (unpatched master, plperl enabled): CREATE FUNCTION perl_huge_text() RETURNS text LANGUAGE plperl AS $$ return 'x' x (1024 * 1024 * 1024); $$; SELECT perl_huge_text(); On a container limited to about 768MB RAM, CREATE FUNCTION alone is enough to lose the backend: LOG: client backend (PID ...) was terminated by signal 9: Killed DETAIL: Failed process was running: CREATE OR REPLACE FUNCTION ... With plenty of free RAM the same code may succeed instead, which I think shows missing enforcement rather than an intentional "no limit" design: other PL/Perl paths already enforce bounds (MAXDIM, AV_SIZE_MAX for SPI results, max_stack_depth in recursive conversion), but sv2cstr() had none. This patch rejects Perl strings larger than work_mem * 1024 bytes, capped by MaxAllocSize, before copying them through sv2cstr(). That follows the same work_mem-based pattern used elsewhere in the backend for per-query working storage. The check is done after SvPVutf8() has reported the length but before utf_u2e() allocates the database-encoding copy. A plperl regression test returns a 16MB string with the default 4MB work_mem and expects: ERROR: Perl value exceeds maximum allowed size (4194304 bytes) HINT: Increase work_mem or reduce the result size. Legitimate functions that need to move more data can raise work_mem for the session, consistent with other operations bounded by that GUC. Comments welcome. -- Regards, Andrey Rachitskiy ^ permalink raw reply [nested|flat] 244+ messages in thread
* Re: [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-07 01:56 Tom Lane <[email protected]> parent: Andrey Rachitskiy <[email protected]> 0 siblings, 1 reply; 244+ messages in thread From: Tom Lane @ 2026-07-07 01:56 UTC (permalink / raw) To: Andrey Rachitskiy <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]> Andrey Rachitskiy <[email protected]> writes: > When a PL/Perl function returns a large text value, sv2cstr() copies the > entire Perl string into backend memory with no size check. The helper > is used on the path from Perl return values and SPI arguments to > PostgreSQL text datums; it simply palloc()s a copy after SvPVutf8(). > A user who is allowed to create untrusted PL/Perl functions can > therefore force the backend to allocate strings far larger than any > session limit. On a memory-constrained host this can get the backend > process killed by the OOM killer (SIGKILL) rather than raising a > catchable PostgreSQL error. This is true of very many operations in PG, not only PL/Perl. Our general answer to that is to disable memory overcommit so that the OOM killer won't apply. One should also note that the same PL/Perl function can (try to) allocate enormous amounts of memory entirely within Perl, where we have no ability to stop it. I don't see how constraining the size of a function result string helps noticeably. > This patch rejects Perl strings larger than work_mem * 1024 bytes, Our normal understanding of work_mem is that it's a point beyond which we'll spill to disk, or otherwise try to reduce our memory consumption at the cost of longer runtime. Not a point at which an outright query failure is OK. So, even if I thought this were something we should address, I don't believe this is an appropriate approach to a fix. regards, tom lane ^ permalink raw reply [nested|flat] 244+ messages in thread
* Re: [PATCH] Limit PL/Perl scalar copies to work_mem @ 2026-07-07 05:44 Andrey Rachitskiy <[email protected]> parent: Tom Lane <[email protected]> 0 siblings, 0 replies; 244+ messages in thread From: Andrey Rachitskiy @ 2026-07-07 05:44 UTC (permalink / raw) To: Tom Lane <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Nikolay Shaplov <[email protected]> Thanks for the review, Tom. You're right that work_mem is a poor fit for a hard failure here, and more generally that this isn't the sort of problem PL/Perl can solve with a small boundary check alone. I should have raised the idea on the list for discussion before sending a patch — I'll do that next time rather than charging ahead with a fix. Thanks for the feedback. On Mon, 06 Jul 2026 21:56:17 -0400, Tom Lane <[email protected]> wrote: > Andrey Rachitskiy <[email protected]> writes: > > When a PL/Perl function returns a large text value, sv2cstr() > > copies the entire Perl string into backend memory with no size > > check. The helper is used on the path from Perl return values and > > SPI arguments to PostgreSQL text datums; it simply palloc()s a copy > > after SvPVutf8(). A user who is allowed to create untrusted PL/Perl > > functions can therefore force the backend to allocate strings far > > larger than any session limit. On a memory-constrained host this > > can get the backend process killed by the OOM killer (SIGKILL) > > rather than raising a catchable PostgreSQL error. > > This is true of very many operations in PG, not only PL/Perl. > Our general answer to that is to disable memory overcommit > so that the OOM killer won't apply. One should also note that > the same PL/Perl function can (try to) allocate enormous amounts > of memory entirely within Perl, where we have no ability to stop > it. I don't see how constraining the size of a function result > string helps noticeably. > > > This patch rejects Perl strings larger than work_mem * 1024 bytes, > > Our normal understanding of work_mem is that it's a point beyond which > we'll spill to disk, or otherwise try to reduce our memory consumption > at the cost of longer runtime. Not a point at which an outright query > failure is OK. > > So, even if I thought this were something we should address, > I don't believe this is an appropriate approach to a fix. > > regards, tom lane -- Regards, Andrey Rachitskiy ^ permalink raw reply [nested|flat] 244+ messages in thread
end of thread, other threads:[~2026-07-07 05:44 UTC | newest] Thread overview: 244+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2023-09-02 06:32 [PATCH v5 5/7] Row pattern recognition patch (docs). Tatsuo Ishii <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:13 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-06 22:41 [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]> 2026-07-07 01:56 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Tom Lane <[email protected]> 2026-07-07 05:44 ` Re: [PATCH] Limit PL/Perl scalar copies to work_mem Andrey Rachitskiy <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox