BUG #18767: Inconsistency in result of a Plpgsql GET DIAGNOSTICS PG_CONTEXT instruction.

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: phb(dot)emaj(at)free(dot)fr
Subject: BUG #18767: Inconsistency in result of a Plpgsql GET DIAGNOSTICS PG_CONTEXT instruction.
Date: 2025-01-06 08:24:33
Message-ID: 18767-e36a4af08c98f883@postgresql.org
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 18767
Logged by: Philippe BEAUDOIN
Email address: phb(dot)emaj(at)free(dot)fr
PostgreSQL version: 17.2
Operating system: Linux
Description:

I use GET DIAGNOSTICS PG_CONTEXT in some sensitive plpgsql functions that
are declared SECURITY DEFINER in order to be sure that the caller belongs to
a list of known callers. The returned stack contains the list of called
functions, with their schema prefix and their parameters format.
But I discovered that when the schema holding these functions is created
while it is in the user's search_path, the returned stack doesn't contain
the schema prefix anymore for the caller functions.
Here is a small test case that reproduces the issue.

A psql script :
select version();
drop schema if exists tst cascade;
create schema tst;
create function tst.caller1() returns text language plpgsql as
$$ BEGIN return tst.caller2(); END; $$;
create function tst.caller2() returns text language plpgsql as
$$ BEGIN return tst.called(); END; $$;
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
DECLARE
v_stack TEXT;
BEGIN
GET DIAGNOSTICS v_stack = PG_CONTEXT;
IF v_stack NOT LIKE '%tst.caller2()%' THEN
RAISE WARNING 'Beware: the calling function is not the expected
one!';
END IF;
RETURN v_stack;
END;
$$;
set search_path = tst;
show search_path;
select caller1() as stack_in_called;
reset search_path;
show search_path;
select tst.caller1() as stack_in_called;

A simple shell script :
echo "=========================================================="
echo " The standart run"
echo "=========================================================="
psql -a -f test_stack.sql
echo "=========================================================="
echo "The issue: a search_path is set before creating structures"
echo "=========================================================="
psql -a -c "SET search_path = tst;" -f test_stack.sql

And the output result :
==========================================================
The standart run
==========================================================
select version();
version

-------------------------------------------------------------------------------------------------------
PostgreSQL 17.2 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu
13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit
(1 row)

drop schema if exists tst cascade;
psql:test_stack.sql:2: NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to function tst.caller1()
drop cascades to function tst.caller2()
drop cascades to function tst.called()
DROP SCHEMA
create schema tst;
CREATE SCHEMA
create function tst.caller1() returns text language plpgsql as
$$ BEGIN return tst.caller2(); END; $$;
CREATE FUNCTION
create function tst.caller2() returns text language plpgsql as
$$ BEGIN return tst.called(); END; $$;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
DECLARE
v_stack TEXT;
BEGIN
GET DIAGNOSTICS v_stack = PG_CONTEXT;
IF v_stack NOT LIKE '%tst.caller2()%' THEN
RAISE WARNING 'Beware: the calling function is not the expected
one!';
END IF;
RETURN v_stack;
END;
$$;
CREATE FUNCTION
set search_path = tst;
SET
show search_path;
search_path
-------------
tst
(1 row)

select caller1() as stack_in_called;
stack_in_called
----------------------------------------------------------
PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
PL/pgSQL function tst.caller2() line 1 at RETURN +
PL/pgSQL function tst.caller1() line 1 at RETURN
(1 row)

reset search_path;
RESET
show search_path;
search_path
-----------------
"$user", public
(1 row)

select tst.caller1() as stack_in_called;
stack_in_called
----------------------------------------------------------
PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
PL/pgSQL function tst.caller2() line 1 at RETURN +
PL/pgSQL function tst.caller1() line 1 at RETURN
(1 row)

==========================================================
The issue: a search_path is set before creating structures
==========================================================
SET search_path = tst;
SET
select version();
version

-------------------------------------------------------------------------------------------------------
PostgreSQL 17.2 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu
13.3.0-6ubuntu2~24.04) 13.3.0, 64-bit
(1 row)

drop schema if exists tst cascade;
psql:test_stack.sql:2: NOTICE: drop cascades to 3 other objects
DETAIL: drop cascades to function caller1()
drop cascades to function caller2()
drop cascades to function called()
DROP SCHEMA
create schema tst;
CREATE SCHEMA
create function tst.caller1() returns text language plpgsql as
$$ BEGIN return tst.caller2(); END; $$;
CREATE FUNCTION
create function tst.caller2() returns text language plpgsql as
$$ BEGIN return tst.called(); END; $$;
CREATE FUNCTION
CREATE OR REPLACE FUNCTION tst.called()
RETURNS text LANGUAGE plpgsql
SECURITY DEFINER SET search_path = pg_catalog, pg_temp AS
$$
DECLARE
v_stack TEXT;
BEGIN
GET DIAGNOSTICS v_stack = PG_CONTEXT;
IF v_stack NOT LIKE '%tst.caller2()%' THEN
RAISE WARNING 'Beware: the calling function is not the expected
one!';
END IF;
RETURN v_stack;
END;
$$;
CREATE FUNCTION
set search_path = tst;
SET
show search_path;
search_path
-------------
tst
(1 row)

select caller1() as stack_in_called;
psql:test_stack.sql:24: WARNING: Beware: the calling function is not the
expected one!
stack_in_called
----------------------------------------------------------
PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
PL/pgSQL function caller2() line 1 at RETURN +
PL/pgSQL function caller1() line 1 at RETURN
(1 row)

reset search_path;
RESET
show search_path;
search_path
-----------------
"$user", public
(1 row)

select tst.caller1() as stack_in_called;
psql:test_stack.sql:27: WARNING: Beware: the calling function is not the
expected one!
stack_in_called
----------------------------------------------------------
PL/pgSQL function tst.called() line 5 at GET DIAGNOSTICS+
PL/pgSQL function caller2() line 1 at RETURN +
PL/pgSQL function caller1() line 1 at RETURN
(1 row)

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Jan Kort 2025-01-06 08:24:55 Re: BUG #18766: not exists sometimes gives too few records
Previous Message David Rowley 2025-01-04 12:15:11 Re: BUG #18766: not exists sometimes gives too few records