Skip to content

Commit 81ead89

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 af3de2d commit 81ead89

File tree

3 files changed

+99
-33
lines changed

3 files changed

+99
-33
lines changed

src/pl/plperl/expected/plperl.out

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -693,3 +693,21 @@ $$ LANGUAGE plperl;
693693
SELECT text_scalarref();
694694
ERROR: PL/Perl function must return reference to hash or array
695695
CONTEXT: PL/Perl function "text_scalarref"
696+
-- check safe behavior when a function body is replaced during execution
697+
CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
698+
spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
699+
spi_exec_query('select self_modify(42) AS a');
700+
return $_[0] * 2;
701+
$$ LANGUAGE plperl;
702+
SELECT self_modify(42);
703+
self_modify
704+
-------------
705+
84
706+
(1 row)
707+
708+
SELECT self_modify(42);
709+
self_modify
710+
-------------
711+
126
712+
(1 row)
713+

src/pl/plperl/plperl.c

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ PG_MODULE_MAGIC;
6969
*
7070
* The plperl_interp_desc structs are kept in a Postgres hash table indexed
7171
* by userid OID, with OID 0 used for the single untrusted interpreter.
72+
* Once created, an interpreter is kept for the life of the process.
7273
*
7374
* We start out by creating a "held" interpreter, which we initialize
7475
* only as far as we can do without deciding if it will be trusted or
@@ -94,28 +95,44 @@ typedef struct plperl_interp_desc
9495

9596
/**********************************************************************
9697
* The information we cache about loaded procedures
98+
*
99+
* The refcount field counts the struct's reference from the hash table shown
100+
* below, plus one reference for each function call level that is using the
101+
* struct. We can release the struct, and the associated Perl sub, when the
102+
* refcount goes to zero.
97103
**********************************************************************/
98104
typedef struct plperl_proc_desc
99105
{
100106
char *proname; /* user name of procedure */
101-
TransactionId fn_xmin;
107+
TransactionId fn_xmin; /* xmin/TID of procedure's pg_proc tuple */
102108
ItemPointerData fn_tid;
109+
int refcount; /* reference count of this struct */
110+
SV *reference; /* CODE reference for Perl sub */
103111
plperl_interp_desc *interp; /* interpreter it's created in */
104-
bool fn_readonly;
105-
bool lanpltrusted;
112+
bool fn_readonly; /* is function readonly (not volatile)? */
113+
bool lanpltrusted; /* is it plperl, rather than plperlu? */
106114
bool fn_retistuple; /* true, if function returns tuple */
107115
bool fn_retisset; /* true, if function returns set */
108116
bool fn_retisarray; /* true if function returns array */
117+
/* Conversion info for function's result type: */
109118
Oid result_oid; /* Oid of result type */
110119
FmgrInfo result_in_func; /* I/O function and arg for result type */
111120
Oid result_typioparam;
121+
/* Conversion info for function's argument types: */
112122
int nargs;
113123
FmgrInfo arg_out_func[FUNC_MAX_ARGS];
114124
bool arg_is_rowtype[FUNC_MAX_ARGS];
115125
Oid arg_arraytype[FUNC_MAX_ARGS]; /* InvalidOid if not an array */
116-
SV *reference;
117126
} plperl_proc_desc;
118127

128+
#define increment_prodesc_refcount(prodesc) \
129+
((prodesc)->refcount++)
130+
#define decrement_prodesc_refcount(prodesc) \
131+
do { \
132+
if (--((prodesc)->refcount) <= 0) \
133+
free_plperl_function(prodesc); \
134+
} while(0)
135+
119136
/**********************************************************************
120137
* For speedy lookup, we maintain a hash table mapping from
121138
* function OID + trigger flag + user OID to plperl_proc_desc pointers.
@@ -237,6 +254,8 @@ static void set_interp_require(bool trusted);
237254
static Datum plperl_func_handler(PG_FUNCTION_ARGS);
238255
static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
239256

257+
static void free_plperl_function(plperl_proc_desc *prodesc);
258+
240259
static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
241260

242261
static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
@@ -1680,19 +1699,24 @@ plperl_call_handler(PG_FUNCTION_ARGS)
16801699

16811700
PG_TRY();
16821701
{
1702+
current_call_data = NULL;
16831703
if (CALLED_AS_TRIGGER(fcinfo))
16841704
retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
16851705
else
16861706
retval = plperl_func_handler(fcinfo);
16871707
}
16881708
PG_CATCH();
16891709
{
1710+
if (current_call_data && current_call_data->prodesc)
1711+
decrement_prodesc_refcount(current_call_data->prodesc);
16901712
current_call_data = save_call_data;
16911713
activate_interpreter(oldinterp);
16921714
PG_RE_THROW();
16931715
}
16941716
PG_END_TRY();
16951717

1718+
if (current_call_data && current_call_data->prodesc)
1719+
decrement_prodesc_refcount(current_call_data->prodesc);
16961720
current_call_data = save_call_data;
16971721
activate_interpreter(oldinterp);
16981722
return retval;
@@ -1744,14 +1768,15 @@ plperl_inline_handler(PG_FUNCTION_ARGS)
17441768
desc.nargs = 0;
17451769
desc.reference = NULL;
17461770

1747-
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
1748-
current_call_data->fcinfo = &fake_fcinfo;
1749-
current_call_data->prodesc = &desc;
1750-
17511771
PG_TRY();
17521772
{
17531773
SV *perlret;
17541774

1775+
current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
1776+
current_call_data->fcinfo = &fake_fcinfo;
1777+
current_call_data->prodesc = &desc;
1778+
/* we do not bother with refcounting the fake prodesc */
1779+
17551780
if (SPI_connect() != SPI_OK_CONNECT)
17561781
elog(ERROR, "could not connect to SPI manager");
17571782

@@ -2145,6 +2170,7 @@ plperl_func_handler(PG_FUNCTION_ARGS)
21452170

21462171
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
21472172
current_call_data->prodesc = prodesc;
2173+
increment_prodesc_refcount(prodesc);
21482174

21492175
/* Set a callback for error reporting */
21502176
pl_error_context.callback = plperl_exec_callback;
@@ -2265,6 +2291,7 @@ plperl_trigger_handler(PG_FUNCTION_ARGS)
22652291
/* Find or compile the function */
22662292
prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
22672293
current_call_data->prodesc = prodesc;
2294+
increment_prodesc_refcount(prodesc);
22682295

22692296
/* Set a callback for error reporting */
22702297
pl_error_context.callback = plperl_exec_callback;
@@ -2374,23 +2401,35 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
23742401

23752402
/* Otherwise, unlink the obsoleted entry from the hashtable ... */
23762403
proc_ptr->proc_ptr = NULL;
2377-
/* ... and throw it away */
2378-
if (prodesc->reference)
2379-
{
2380-
plperl_interp_desc *oldinterp = plperl_active_interp;
2381-
2382-
activate_interpreter(prodesc->interp);
2383-
SvREFCNT_dec(prodesc->reference);
2384-
activate_interpreter(oldinterp);
2385-
}
2386-
free(prodesc->proname);
2387-
free(prodesc);
2404+
/* ... and release the corresponding refcount, probably deleting it */
2405+
decrement_prodesc_refcount(prodesc);
23882406
}
23892407

23902408
return false;
23912409
}
23922410

23932411

2412+
static void
2413+
free_plperl_function(plperl_proc_desc *prodesc)
2414+
{
2415+
Assert(prodesc->refcount <= 0);
2416+
/* Release CODE reference, if we have one, from the appropriate interp */
2417+
if (prodesc->reference)
2418+
{
2419+
plperl_interp_desc *oldinterp = plperl_active_interp;
2420+
2421+
activate_interpreter(prodesc->interp);
2422+
SvREFCNT_dec(prodesc->reference);
2423+
activate_interpreter(oldinterp);
2424+
}
2425+
/* Get rid of what we conveniently can of our own structs */
2426+
/* (FmgrInfo subsidiary info will get leaked ...) */
2427+
if (prodesc->proname)
2428+
free(prodesc->proname);
2429+
free(prodesc);
2430+
}
2431+
2432+
23942433
static plperl_proc_desc *
23952434
compile_plperl_function(Oid fn_oid, bool is_trigger)
23962435
{
@@ -2461,12 +2500,17 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
24612500
ereport(ERROR,
24622501
(errcode(ERRCODE_OUT_OF_MEMORY),
24632502
errmsg("out of memory")));
2503+
/* Initialize all fields to 0 so free_plperl_function is safe */
24642504
MemSet(prodesc, 0, sizeof(plperl_proc_desc));
2505+
24652506
prodesc->proname = strdup(NameStr(procStruct->proname));
24662507
if (prodesc->proname == NULL)
2508+
{
2509+
free_plperl_function(prodesc);
24672510
ereport(ERROR,
24682511
(errcode(ERRCODE_OUT_OF_MEMORY),
24692512
errmsg("out of memory")));
2513+
}
24702514
prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
24712515
prodesc->fn_tid = procTup->t_self;
24722516

@@ -2481,8 +2525,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
24812525
ObjectIdGetDatum(procStruct->prolang));
24822526
if (!HeapTupleIsValid(langTup))
24832527
{
2484-
free(prodesc->proname);
2485-
free(prodesc);
2528+
free_plperl_function(prodesc);
24862529
elog(ERROR, "cache lookup failed for language %u",
24872530
procStruct->prolang);
24882531
}
@@ -2501,8 +2544,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25012544
ObjectIdGetDatum(procStruct->prorettype));
25022545
if (!HeapTupleIsValid(typeTup))
25032546
{
2504-
free(prodesc->proname);
2505-
free(prodesc);
2547+
free_plperl_function(prodesc);
25062548
elog(ERROR, "cache lookup failed for type %u",
25072549
procStruct->prorettype);
25082550
}
@@ -2516,17 +2558,15 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25162558
/* okay */ ;
25172559
else if (procStruct->prorettype == TRIGGEROID)
25182560
{
2519-
free(prodesc->proname);
2520-
free(prodesc);
2561+
free_plperl_function(prodesc);
25212562
ereport(ERROR,
25222563
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
25232564
errmsg("trigger functions can only be called "
25242565
"as triggers")));
25252566
}
25262567
else
25272568
{
2528-
free(prodesc->proname);
2529-
free(prodesc);
2569+
free_plperl_function(prodesc);
25302570
ereport(ERROR,
25312571
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
25322572
errmsg("PL/Perl functions cannot return type %s",
@@ -2561,8 +2601,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25612601
ObjectIdGetDatum(procStruct->proargtypes.values[i]));
25622602
if (!HeapTupleIsValid(typeTup))
25632603
{
2564-
free(prodesc->proname);
2565-
free(prodesc);
2604+
free_plperl_function(prodesc);
25662605
elog(ERROR, "cache lookup failed for type %u",
25672606
procStruct->proargtypes.values[i]);
25682607
}
@@ -2572,8 +2611,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
25722611
if (typeStruct->typtype == TYPTYPE_PSEUDO &&
25732612
procStruct->proargtypes.values[i] != RECORDOID)
25742613
{
2575-
free(prodesc->proname);
2576-
free(prodesc);
2614+
free_plperl_function(prodesc);
25772615
ereport(ERROR,
25782616
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
25792617
errmsg("PL/Perl functions cannot accept type %s",
@@ -2626,8 +2664,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
26262664
pfree(proc_source);
26272665
if (!prodesc->reference) /* can this happen? */
26282666
{
2629-
free(prodesc->proname);
2630-
free(prodesc);
2667+
free_plperl_function(prodesc);
26312668
elog(ERROR, "could not create PL/Perl internal procedure");
26322669
}
26332670

@@ -2639,6 +2676,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
26392676
proc_ptr = hash_search(plperl_proc_hash, &proc_key,
26402677
HASH_ENTER, NULL);
26412678
proc_ptr->proc_ptr = prodesc;
2679+
increment_prodesc_refcount(prodesc);
26422680
}
26432681

26442682
/* restore previous error callback */

src/pl/plperl/sql/plperl.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,3 +462,13 @@ CREATE OR REPLACE FUNCTION text_scalarref() RETURNS text AS $$
462462
$$ LANGUAGE plperl;
463463

464464
SELECT text_scalarref();
465+
466+
-- check safe behavior when a function body is replaced during execution
467+
CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
468+
spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
469+
spi_exec_query('select self_modify(42) AS a');
470+
return $_[0] * 2;
471+
$$ LANGUAGE plperl;
472+
473+
SELECT self_modify(42);
474+
SELECT self_modify(42);

0 commit comments

Comments
 (0)