I wrote:
> Hm.  Well, I can definitely reproduce a segfault using this example:

> CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
>    spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS 
> INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
>    spi_exec_query('select self_modify(42) AS a');
>    return $_[0] * 2;
> $$ LANGUAGE plperl;

> select self_modify(42);

> So I think we need to institute a reference counting scheme for
> the plperl_proc_desc records ...

The attached patch fixes the problem I'm seeing.  I am not sure whether
it fixes what you saw; the crash you showed is in the right place, but
unless there was a recursive call to a pl/perl function, I don't see
how the existing code could have freed the prodesc too soon.

                        regards, tom lane

diff --git a/src/pl/plperl/expected/plperl.out b/src/pl/plperl/expected/plperl.out
index 906dc15e0ca097ec962c6dce9a08b29cb31d35b5..29c1d11c447e6087fee85565c0bf458cbc21486d 100644
*** a/src/pl/plperl/expected/plperl.out
--- b/src/pl/plperl/expected/plperl.out
*************** $$ LANGUAGE plperl;
*** 693,695 ****
--- 693,713 ----
  SELECT text_scalarref();
  ERROR:  PL/Perl function must return reference to hash or array
  CONTEXT:  PL/Perl function "text_scalarref"
+ -- check safe behavior when a function body is replaced during execution
+ CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
+    spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
+    spi_exec_query('select self_modify(42) AS a');
+    return $_[0] * 2;
+ $$ LANGUAGE plperl;
+ SELECT self_modify(42);
+  self_modify 
+ -------------
+           84
+ (1 row)
+ 
+ SELECT self_modify(42);
+  self_modify 
+ -------------
+          126
+ (1 row)
+ 
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index bfa805d944f7b309428b6818b7160215c1b8e9fc..2c39edac5bd93ca8b446aafb88a17e0b7a5088db 100644
*** a/src/pl/plperl/plperl.c
--- b/src/pl/plperl/plperl.c
*************** PG_MODULE_MAGIC;
*** 70,75 ****
--- 70,76 ----
   *
   * The plperl_interp_desc structs are kept in a Postgres hash table indexed
   * by userid OID, with OID 0 used for the single untrusted interpreter.
+  * Once created, an interpreter is kept for the life of the process.
   *
   * We start out by creating a "held" interpreter, which we initialize
   * only as far as we can do without deciding if it will be trusted or
*************** typedef struct plperl_interp_desc
*** 95,122 ****
  
  /**********************************************************************
   * The information we cache about loaded procedures
   **********************************************************************/
  typedef struct plperl_proc_desc
  {
  	char	   *proname;		/* user name of procedure */
! 	TransactionId fn_xmin;
  	ItemPointerData fn_tid;
  	plperl_interp_desc *interp; /* interpreter it's created in */
! 	bool		fn_readonly;
! 	bool		lanpltrusted;
  	bool		fn_retistuple;	/* true, if function returns tuple */
  	bool		fn_retisset;	/* true, if function returns set */
  	bool		fn_retisarray;	/* true if function returns array */
  	Oid			result_oid;		/* Oid of result type */
  	FmgrInfo	result_in_func; /* I/O function and arg for result type */
  	Oid			result_typioparam;
  	int			nargs;
  	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];
  	bool		arg_is_rowtype[FUNC_MAX_ARGS];
  	Oid			arg_arraytype[FUNC_MAX_ARGS];	/* InvalidOid if not an array */
- 	SV		   *reference;
  } plperl_proc_desc;
  
  /**********************************************************************
   * For speedy lookup, we maintain a hash table mapping from
   * function OID + trigger flag + user OID to plperl_proc_desc pointers.
--- 96,139 ----
  
  /**********************************************************************
   * The information we cache about loaded procedures
+  *
+  * The refcount field counts the struct's reference from the hash table shown
+  * below, plus one reference for each function call level that is using the
+  * struct.  We can release the struct, and the associated Perl sub, when the
+  * refcount goes to zero.
   **********************************************************************/
  typedef struct plperl_proc_desc
  {
  	char	   *proname;		/* user name of procedure */
! 	TransactionId fn_xmin;		/* xmin/TID of procedure's pg_proc tuple */
  	ItemPointerData fn_tid;
+ 	int			refcount;		/* reference count of this struct */
+ 	SV		   *reference;		/* CODE reference for Perl sub */
  	plperl_interp_desc *interp; /* interpreter it's created in */
! 	bool		fn_readonly;	/* is function readonly (not volatile)? */
! 	bool		lanpltrusted;	/* is it plperl, rather than plperlu? */
  	bool		fn_retistuple;	/* true, if function returns tuple */
  	bool		fn_retisset;	/* true, if function returns set */
  	bool		fn_retisarray;	/* true if function returns array */
+ 	/* Conversion info for function's result type: */
  	Oid			result_oid;		/* Oid of result type */
  	FmgrInfo	result_in_func; /* I/O function and arg for result type */
  	Oid			result_typioparam;
+ 	/* Conversion info for function's argument types: */
  	int			nargs;
  	FmgrInfo	arg_out_func[FUNC_MAX_ARGS];
  	bool		arg_is_rowtype[FUNC_MAX_ARGS];
  	Oid			arg_arraytype[FUNC_MAX_ARGS];	/* InvalidOid if not an array */
  } plperl_proc_desc;
  
+ #define increment_prodesc_refcount(prodesc)  \
+ 	((prodesc)->refcount++)
+ #define decrement_prodesc_refcount(prodesc)  \
+ 	do { \
+ 		if (--((prodesc)->refcount) <= 0) \
+ 			free_plperl_function(prodesc); \
+ 	} while(0)
+ 
  /**********************************************************************
   * For speedy lookup, we maintain a hash table mapping from
   * function OID + trigger flag + user OID to plperl_proc_desc pointers.
*************** static void set_interp_require(bool trus
*** 238,243 ****
--- 255,262 ----
  static Datum plperl_func_handler(PG_FUNCTION_ARGS);
  static Datum plperl_trigger_handler(PG_FUNCTION_ARGS);
  
+ static void free_plperl_function(plperl_proc_desc *prodesc);
+ 
  static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger);
  
  static SV  *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
*************** plperl_call_handler(PG_FUNCTION_ARGS)
*** 1689,1694 ****
--- 1708,1714 ----
  
  	PG_TRY();
  	{
+ 		current_call_data = NULL;
  		if (CALLED_AS_TRIGGER(fcinfo))
  			retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
  		else
*************** plperl_call_handler(PG_FUNCTION_ARGS)
*** 1696,1707 ****
--- 1716,1731 ----
  	}
  	PG_CATCH();
  	{
+ 		if (current_call_data && current_call_data->prodesc)
+ 			decrement_prodesc_refcount(current_call_data->prodesc);
  		current_call_data = save_call_data;
  		activate_interpreter(oldinterp);
  		PG_RE_THROW();
  	}
  	PG_END_TRY();
  
+ 	if (current_call_data && current_call_data->prodesc)
+ 		decrement_prodesc_refcount(current_call_data->prodesc);
  	current_call_data = save_call_data;
  	activate_interpreter(oldinterp);
  	return retval;
*************** plperl_inline_handler(PG_FUNCTION_ARGS)
*** 1753,1766 ****
  	desc.nargs = 0;
  	desc.reference = NULL;
  
- 	current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
- 	current_call_data->fcinfo = &fake_fcinfo;
- 	current_call_data->prodesc = &desc;
- 
  	PG_TRY();
  	{
  		SV		   *perlret;
  
  		if (SPI_connect() != SPI_OK_CONNECT)
  			elog(ERROR, "could not connect to SPI manager");
  
--- 1777,1791 ----
  	desc.nargs = 0;
  	desc.reference = NULL;
  
  	PG_TRY();
  	{
  		SV		   *perlret;
  
+ 		current_call_data = (plperl_call_data *) palloc0(sizeof(plperl_call_data));
+ 		current_call_data->fcinfo = &fake_fcinfo;
+ 		current_call_data->prodesc = &desc;
+ 		/* we do not bother with refcounting the fake prodesc */
+ 
  		if (SPI_connect() != SPI_OK_CONNECT)
  			elog(ERROR, "could not connect to SPI manager");
  
*************** plperl_func_handler(PG_FUNCTION_ARGS)
*** 2154,2159 ****
--- 2179,2185 ----
  
  	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false);
  	current_call_data->prodesc = prodesc;
+ 	increment_prodesc_refcount(prodesc);
  
  	/* Set a callback for error reporting */
  	pl_error_context.callback = plperl_exec_callback;
*************** plperl_trigger_handler(PG_FUNCTION_ARGS)
*** 2274,2279 ****
--- 2300,2306 ----
  	/* Find or compile the function */
  	prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true);
  	current_call_data->prodesc = prodesc;
+ 	increment_prodesc_refcount(prodesc);
  
  	/* Set a callback for error reporting */
  	pl_error_context.callback = plperl_exec_callback;
*************** validate_plperl_function(plperl_proc_ptr
*** 2383,2405 ****
  
  		/* Otherwise, unlink the obsoleted entry from the hashtable ... */
  		proc_ptr->proc_ptr = NULL;
! 		/* ... and throw it away */
! 		if (prodesc->reference)
! 		{
! 			plperl_interp_desc *oldinterp = plperl_active_interp;
! 
! 			activate_interpreter(prodesc->interp);
! 			SvREFCNT_dec(prodesc->reference);
! 			activate_interpreter(oldinterp);
! 		}
! 		free(prodesc->proname);
! 		free(prodesc);
  	}
  
  	return false;
  }
  
  
  static plperl_proc_desc *
  compile_plperl_function(Oid fn_oid, bool is_trigger)
  {
--- 2410,2444 ----
  
  		/* Otherwise, unlink the obsoleted entry from the hashtable ... */
  		proc_ptr->proc_ptr = NULL;
! 		/* ... and release the corresponding refcount, probably deleting it */
! 		decrement_prodesc_refcount(prodesc);
  	}
  
  	return false;
  }
  
  
+ static void
+ free_plperl_function(plperl_proc_desc *prodesc)
+ {
+ 	Assert(prodesc->refcount <= 0);
+ 	/* Release CODE reference, if we have one, from the appropriate interp */
+ 	if (prodesc->reference)
+ 	{
+ 		plperl_interp_desc *oldinterp = plperl_active_interp;
+ 
+ 		activate_interpreter(prodesc->interp);
+ 		SvREFCNT_dec(prodesc->reference);
+ 		activate_interpreter(oldinterp);
+ 	}
+ 	/* Get rid of what we conveniently can of our own structs */
+ 	/* (FmgrInfo subsidiary info will get leaked ...) */
+ 	if (prodesc->proname)
+ 		free(prodesc->proname);
+ 	free(prodesc);
+ }
+ 
+ 
  static plperl_proc_desc *
  compile_plperl_function(Oid fn_oid, bool is_trigger)
  {
*************** compile_plperl_function(Oid fn_oid, bool
*** 2470,2481 ****
--- 2509,2525 ----
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory")));
+ 		/* Initialize all fields to 0 so free_plperl_function is safe */
  		MemSet(prodesc, 0, sizeof(plperl_proc_desc));
+ 
  		prodesc->proname = strdup(NameStr(procStruct->proname));
  		if (prodesc->proname == NULL)
+ 		{
+ 			free_plperl_function(prodesc);
  			ereport(ERROR,
  					(errcode(ERRCODE_OUT_OF_MEMORY),
  					 errmsg("out of memory")));
+ 		}
  		prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
  		prodesc->fn_tid = procTup->t_self;
  
*************** compile_plperl_function(Oid fn_oid, bool
*** 2490,2497 ****
  								  ObjectIdGetDatum(procStruct->prolang));
  		if (!HeapTupleIsValid(langTup))
  		{
! 			free(prodesc->proname);
! 			free(prodesc);
  			elog(ERROR, "cache lookup failed for language %u",
  				 procStruct->prolang);
  		}
--- 2534,2540 ----
  								  ObjectIdGetDatum(procStruct->prolang));
  		if (!HeapTupleIsValid(langTup))
  		{
! 			free_plperl_function(prodesc);
  			elog(ERROR, "cache lookup failed for language %u",
  				 procStruct->prolang);
  		}
*************** compile_plperl_function(Oid fn_oid, bool
*** 2510,2517 ****
  								ObjectIdGetDatum(procStruct->prorettype));
  			if (!HeapTupleIsValid(typeTup))
  			{
! 				free(prodesc->proname);
! 				free(prodesc);
  				elog(ERROR, "cache lookup failed for type %u",
  					 procStruct->prorettype);
  			}
--- 2553,2559 ----
  								ObjectIdGetDatum(procStruct->prorettype));
  			if (!HeapTupleIsValid(typeTup))
  			{
! 				free_plperl_function(prodesc);
  				elog(ERROR, "cache lookup failed for type %u",
  					 procStruct->prorettype);
  			}
*************** compile_plperl_function(Oid fn_oid, bool
*** 2525,2532 ****
  					 /* okay */ ;
  				else if (procStruct->prorettype == TRIGGEROID)
  				{
! 					free(prodesc->proname);
! 					free(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("trigger functions can only be called "
--- 2567,2573 ----
  					 /* okay */ ;
  				else if (procStruct->prorettype == TRIGGEROID)
  				{
! 					free_plperl_function(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("trigger functions can only be called "
*************** compile_plperl_function(Oid fn_oid, bool
*** 2534,2541 ****
  				}
  				else
  				{
! 					free(prodesc->proname);
! 					free(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("PL/Perl functions cannot return type %s",
--- 2575,2581 ----
  				}
  				else
  				{
! 					free_plperl_function(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("PL/Perl functions cannot return type %s",
*************** compile_plperl_function(Oid fn_oid, bool
*** 2570,2577 ****
  						ObjectIdGetDatum(procStruct->proargtypes.values[i]));
  				if (!HeapTupleIsValid(typeTup))
  				{
! 					free(prodesc->proname);
! 					free(prodesc);
  					elog(ERROR, "cache lookup failed for type %u",
  						 procStruct->proargtypes.values[i]);
  				}
--- 2610,2616 ----
  						ObjectIdGetDatum(procStruct->proargtypes.values[i]));
  				if (!HeapTupleIsValid(typeTup))
  				{
! 					free_plperl_function(prodesc);
  					elog(ERROR, "cache lookup failed for type %u",
  						 procStruct->proargtypes.values[i]);
  				}
*************** compile_plperl_function(Oid fn_oid, bool
*** 2581,2588 ****
  				if (typeStruct->typtype == TYPTYPE_PSEUDO &&
  					procStruct->proargtypes.values[i] != RECORDOID)
  				{
! 					free(prodesc->proname);
! 					free(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("PL/Perl functions cannot accept type %s",
--- 2620,2626 ----
  				if (typeStruct->typtype == TYPTYPE_PSEUDO &&
  					procStruct->proargtypes.values[i] != RECORDOID)
  				{
! 					free_plperl_function(prodesc);
  					ereport(ERROR,
  							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  							 errmsg("PL/Perl functions cannot accept type %s",
*************** compile_plperl_function(Oid fn_oid, bool
*** 2635,2642 ****
  		pfree(proc_source);
  		if (!prodesc->reference)	/* can this happen? */
  		{
! 			free(prodesc->proname);
! 			free(prodesc);
  			elog(ERROR, "could not create PL/Perl internal procedure");
  		}
  
--- 2673,2679 ----
  		pfree(proc_source);
  		if (!prodesc->reference)	/* can this happen? */
  		{
! 			free_plperl_function(prodesc);
  			elog(ERROR, "could not create PL/Perl internal procedure");
  		}
  
*************** compile_plperl_function(Oid fn_oid, bool
*** 2648,2653 ****
--- 2685,2691 ----
  		proc_ptr = hash_search(plperl_proc_hash, &proc_key,
  							   HASH_ENTER, NULL);
  		proc_ptr->proc_ptr = prodesc;
+ 		increment_prodesc_refcount(prodesc);
  	}
  
  	/* restore previous error callback */
diff --git a/src/pl/plperl/sql/plperl.sql b/src/pl/plperl/sql/plperl.sql
index a5e3840dac23667ff2d599c46b6e6887cc6875a8..ad361614c488fa7f04dbd10a6f4844f8cf506d1b 100644
*** a/src/pl/plperl/sql/plperl.sql
--- b/src/pl/plperl/sql/plperl.sql
*************** CREATE OR REPLACE FUNCTION text_scalarre
*** 462,464 ****
--- 462,474 ----
  $$ LANGUAGE plperl;
  
  SELECT text_scalarref();
+ 
+ -- check safe behavior when a function body is replaced during execution
+ CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS $$
+    spi_exec_query('CREATE OR REPLACE FUNCTION self_modify(INTEGER) RETURNS INTEGER AS \'return $_[0] * 3;\' LANGUAGE plperl;');
+    spi_exec_query('select self_modify(42) AS a');
+    return $_[0] * 2;
+ $$ LANGUAGE plperl;
+ 
+ SELECT self_modify(42);
+ SELECT self_modify(42);
-- 
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs

Reply via email to