Skip to content

Commit 0952811

Browse files
committed
Make plperl safe against functions that are redefined while running.
validate_plperl_function() supposed that it could free an old plperl_proc_desc struct immediately upon detecting that it was stale. However, if a plperl function is called recursively, this could result in deleting the struct out from under an outer invocation, leading to misbehavior or crashes. Add a simple reference-count mechanism to ensure that such structs are freed only when the last reference goes away. Per investigation of bug #7516 from Marko Tiikkaja. I am not certain that this error explains his report, because he says he didn't have any recursive calls --- but it's hard to see how else it could have crashed right there. In any case, this definitely fixes some problems in the area. Back-patch to all active branches.
1 parent 25b6df1 commit 0952811

File tree

3 files changed

+98
-33
lines changed

3 files changed

+98
-33
lines changed

src/pl/plperl/expected/plperl.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,3 +597,21 @@ CONTEXT: PL/Perl anonymous code block
597597
DO $do$ use warnings FATAL => qw(void) ; my @y; my $x = sort @y; 1; $do$ LANGUAGE plperl;
598598
ERROR: Useless use of sort in scalar context at line 1.
599599
CONTEXT: PL/Perl anonymous code block
600+
-- check safe behavior when a function body is replaced during execution
601+
CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
602+
spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
603+
spi_exec_query('select self_modify(42) AS a');
604+
return $_[0] * 2;
605+
$$ LANGUAGE plperl;
606+
SELECT self_modify(42);
607+
self_modify
608+
-------------
609+
84
610+
(1 row)
611+
612+
SELECT self_modify(42);
613+
self_modify
614+
-------------
615+
126
616+
(1 row)
617+

src/pl/plperl/plperl.c

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ PG_MODULE_MAGIC;
6767
*
6868
* The plperl_interp_desc structs are kept in a Postgres hash table indexed
6969
* by userid OID, with OID 0 used for the single untrusted interpreter.
70+
* Once created, an interpreter is kept for the life of the process.
7071
*
7172
* We start out by creating a "held" interpreter, which we initialize
7273
* only as far as we can do without deciding if it will be trusted or
@@ -92,27 +93,43 @@ typedef struct plperl_interp_desc
9293

9394
/**********************************************************************
9495
* The information we cache about loaded procedures
96+
*
97+
* The refcount field counts the struct's reference from the hash table shown
98+
* below, plus one reference for each function call level that is using the
99+
* struct. We can release the struct, and the associated Perl sub, when the
100+
* refcount goes to zero.
95101
**********************************************************************/
96102
typedef struct plperl_proc_desc
97103
{
98104
char *proname; /* user name of procedure */
99-
TransactionId fn_xmin;
105+
TransactionId fn_xmin; /* xmin/TID of procedure's pg_proc tuple */
100106
ItemPointerData fn_tid;
107+
int refcount; /* reference count of this struct */
108+
SV *reference; /* CODE reference for Perl sub */
101109
plperl_interp_desc *interp; /* interpreter it's created in */
102-
bool fn_readonly;
103-
bool lanpltrusted;
110+
bool fn_readonly; /* is function readonly (not volatile)? */
111+
bool lanpltrusted; /* is it plperl, rather than plperlu? */
104112
bool fn_retistuple; /* true, if function returns tuple */
105113
bool fn_retisset; /* true, if function returns set */
106114
bool fn_retisarray; /* true if function returns array */
115+
/* Conversion info for function's result type: */
107116
Oid result_oid; /* Oid of result type */
108117
FmgrInfo result_in_func; /* I/O function and arg for result type */
109118
Oid result_typioparam;
119+
/* Conversion info for function's argument types: */
110120
int nargs;
111121
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
112122
bool arg_is_rowtype[FUNC_MAX_ARGS];
113-
SV *reference;
114123
} plperl_proc_desc;
115124

125+
#define increment_prodesc_refcount(prodesc) \
126+
((prodesc)->refcount++)
127+
#define decrement_prodesc_refcount(prodesc) \
128+
do { \
129+
if (--((prodesc)->refcount) <= 0) \
130+
free_plperl_function(prodesc); \
131+
} while(0)
132+
116133
/**********************************************************************
117134
* For speedy lookup, we maintain a hash table mapping from
118135
* function OID + trigger flag + user OID to plperl_proc_desc pointers.
@@ -217,6 +234,8 @@ static void set_interp_require(bool trusted);
217234
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
218235
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
219236

237+
static void free_plperl_function(plperl_proc_desc *prodesc);
238+
220239
static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
221240

222241
static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
@@ -1228,19 +1247,24 @@ plperl_call_handler(PG_FUNCTION_ARGS)
12281247

12291248
PG_TRY();
12301249
{
1250+
current_call_data = NULL;
12311251
if (CALLED_AS_TRIGGER(fcinfo))
12321252
retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
12331253
else
12341254
retval = plperl_func_handler(fcinfo);
12351255
}
12361256
PG_CATCH();
12371257
{
1258+
if (current_call_data && current_call_data->prodesc)
1259+
decrement_prodesc_refcount(current_call_data->prodesc);
12381260
current_call_data = save_call_data;
12391261
activate_interpreter(oldinterp);
12401262
PG_RE_THROW();
12411263
}
12421264
PG_END_TRY();
12431265

1266+
if (current_call_data && current_call_data->prodesc)
1267+
decrement_prodesc_refcount(current_call_data->prodesc);
12441268
current_call_data = save_call_data;
12451269
activate_interpreter(oldinterp);
12461270
return retval;
@@ -1292,14 +1316,15 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
12921316
desc.nargs = 0;
12931317
desc.reference = NULL;
12941318

1295-
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
1296-
current_call_data->fcinfo = &fake_fcinfo;
1297-
current_call_data->prodesc = &desc;
1298-
12991319
PG_TRY();
13001320
{
13011321
SV *perlret;
13021322

1323+
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
1324+
current_call_data->fcinfo = &fake_fcinfo;
1325+
current_call_data->prodesc = &desc;
1326+
/* we do not bother with refcounting the fake prodesc */
1327+
13031328
if (SPI_connect() != SPI_OK_CONNECT)
13041329
elog(ERROR, "could not connect to SPI manager");
13051330

@@ -1659,6 +1684,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
16591684

16601685
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
16611686
current_call_data->prodesc = prodesc;
1687+
increment_prodesc_refcount(prodesc);
16621688

16631689
/* Set a callback for error reporting */
16641690
pl_error_context.callback = plperl_exec_callback;
@@ -1820,6 +1846,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
18201846
/* Find or compile the function */
18211847
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
18221848
current_call_data->prodesc = prodesc;
1849+
increment_prodesc_refcount(prodesc);
18231850

18241851
/* Set a callback for error reporting */
18251852
pl_error_context.callback = plperl_exec_callback;
@@ -1928,23 +1955,35 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
19281955

19291956
/* Otherwise, unlink the obsoleted entry from the hashtable ... */
19301957
proc_ptr->proc_ptr = NULL;
1931-
/* ... and throw it away */
1932-
if (prodesc->reference)
1933-
{
1934-
plperl_interp_desc *oldinterp = plperl_active_interp;
1935-
1936-
activate_interpreter(prodesc->interp);
1937-
SvREFCNT_dec(prodesc->reference);
1938-
activate_interpreter(oldinterp);
1939-
}
1940-
free(prodesc->proname);
1941-
free(prodesc);
1958+
/* ... and release the corresponding refcount, probably deleting it */
1959+
decrement_prodesc_refcount(prodesc);
19421960
}
19431961

19441962
return false;
19451963
}
19461964

19471965

1966+
static void
1967+
free_plperl_function(plperl_proc_desc *prodesc)
1968+
{
1969+
Assert(prodesc->refcount <= 0);
1970+
/* Release CODE reference, if we have one, from the appropriate interp */
1971+
if (prodesc->reference)
1972+
{
1973+
plperl_interp_desc *oldinterp = plperl_active_interp;
1974+
1975+
activate_interpreter(prodesc->interp);
1976+
SvREFCNT_dec(prodesc->reference);
1977+
activate_interpreter(oldinterp);
1978+
}
1979+
/* Get rid of what we conveniently can of our own structs */
1980+
/* (FmgrInfo subsidiary info will get leaked ...) */
1981+
if (prodesc->proname)
1982+
free(prodesc->proname);
1983+
free(prodesc);
1984+
}
1985+
1986+
19481987
static plperl_proc_desc *
19491988
compile_plperl_function(Oid fn_oid, bool is_trigger)
19501989
{
@@ -2015,12 +2054,17 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
20152054
ereport(ERROR,
20162055
(errcode(ERRCODE_OUT_OF_MEMORY),
20172056
errmsg("out of memory")));
2057+
/* Initialize all fields to 0 so free_plperl_function is safe */
20182058
MemSet(prodesc, 0, sizeof(plperl_proc_desc));
2059+
20192060
prodesc->proname = strdup(NameStr(procStruct->proname));
20202061
if (prodesc->proname == NULL)
2062+
{
2063+
free_plperl_function(prodesc);
20212064
ereport(ERROR,
20222065
(errcode(ERRCODE_OUT_OF_MEMORY),
20232066
errmsg("out of memory")));
2067+
}
20242068
prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
20252069
prodesc->fn_tid = procTup->t_self;
20262070

@@ -2035,8 +2079,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
20352079
ObjectIdGetDatum(procStruct->prolang));
20362080
if (!HeapTupleIsValid(langTup))
20372081
{
2038-
free(prodesc->proname);
2039-
free(prodesc);
2082+
free_plperl_function(prodesc);
20402083
elog(ERROR, "cache lookup failed for language %u",
20412084
procStruct->prolang);
20422085
}
@@ -2055,8 +2098,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
20552098
ObjectIdGetDatum(procStruct->prorettype));
20562099
if (!HeapTupleIsValid(typeTup))
20572100
{
2058-
free(prodesc->proname);
2059-
free(prodesc);
2101+
free_plperl_function(prodesc);
20602102
elog(ERROR, "cache lookup failed for type %u",
20612103
procStruct->prorettype);
20622104
}
@@ -2070,17 +2112,15 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
20702112
/* okay */ ;
20712113
else if (procStruct->prorettype == TRIGGEROID)
20722114
{
2073-
free(prodesc->proname);
2074-
free(prodesc);
2115+
free_plperl_function(prodesc);
20752116
ereport(ERROR,
20762117
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20772118
errmsg("trigger functions can only be called "
20782119
"as triggers")));
20792120
}
20802121
else
20812122
{
2082-
free(prodesc->proname);
2083-
free(prodesc);
2123+
free_plperl_function(prodesc);
20842124
ereport(ERROR,
20852125
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20862126
errmsg("PL/Perl functions cannot return type %s",
@@ -2115,8 +2155,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
21152155
ObjectIdGetDatum(procStruct->proargtypes.values[i]));
21162156
if (!HeapTupleIsValid(typeTup))
21172157
{
2118-
free(prodesc->proname);
2119-
free(prodesc);
2158+
free_plperl_function(prodesc);
21202159
elog(ERROR, "cache lookup failed for type %u",
21212160
procStruct->proargtypes.values[i]);
21222161
}
@@ -2125,8 +2164,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
21252164
/* Disallow pseudotype argument */
21262165
if (typeStruct->typtype == TYPTYPE_PSEUDO)
21272166
{
2128-
free(prodesc->proname);
2129-
free(prodesc);
2167+
free_plperl_function(prodesc);
21302168
ereport(ERROR,
21312169
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21322170
errmsg("PL/Perl functions cannot accept type %s",
@@ -2172,8 +2210,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
21722210
pfree(proc_source);
21732211
if (!prodesc->reference) /* can this happen? */
21742212
{
2175-
free(prodesc->proname);
2176-
free(prodesc);
2213+
free_plperl_function(prodesc);
21772214
elog(ERROR, "could not create PL/Perl internal procedure");
21782215
}
21792216

@@ -2185,6 +2222,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
21852222
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
21862223
HASH_ENTER, NULL);
21872224
proc_ptr->proc_ptr = prodesc;
2225+
increment_prodesc_refcount(prodesc);
21882226
}
21892227

21902228
/* restore previous error callback */

src/pl/plperl/sql/plperl.sql

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,12 @@ DO $do$ use strict; my $name = "foo"; my $ref = $$name; $do$ LANGUAGE plperl;
391391
-- yields "ERROR: Useless use of sort in scalar context."
392392
DO $do$ use warnings FATAL => qw(void) ; my @y; my $x = sort @y; 1; $do$ LANGUAGE plperl;
393393

394+
-- check safe behavior when a function body is replaced during execution
395+
CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
396+
spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
397+
spi_exec_query('select self_modify(42) AS a');
398+
return $_[0] * 2;
399+
$$ LANGUAGE plperl;
400+
401+
SELECT self_modify(42);
402+
SELECT self_modify(42);

0 commit comments

Comments
 (0)