Skip to content

Commit c71f01d

Browse files
committed
Make functions schema-safe via pinned search_path
When the extension was installed into a non-default schema, calling its functions failed with "relation ... does not exist" unless the caller happened to have that schema on their search_path. The SECURITY INVOKER functions referenced their tables unqualified and inherited whatever search_path the session had. Pin search_path = @extschema@, pg_temp on every function and procedure (the SECURITY DEFINER helpers already did this), so they always resolve the extension's own tables regardless of the caller's search_path. Also grant pgos_admin USAGE on the install schema so the SECURITY DEFINER locking helpers can reach those tables outside the public schema (PUBLIC only receives that USAGE for public automatically). Adds a schema_install regression test that installs into a dedicated schema, drops that schema from the search_path, and exercises the user/process/memory/file and mutex paths through fully-qualified calls. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ty3Rkif9Vfe95w8TMiKB4b
1 parent d1b9169 commit c71f01d

4 files changed

Lines changed: 168 additions & 47 deletions

File tree

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ REGRESS = pg_os_basic \
1313
lock_file \
1414
load_unload_module \
1515
modules \
16-
locks_security
16+
locks_security \
17+
schema_install
1718
REGRESS_OPTS = --outputdir=$(CURDIR)/tmp_pg_os_regress
1819
PGXS := $(shell $(PG_CONFIG) --pgxs)
1920
include $(PGXS)

expected/schema_install.out

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
-- The extension must work when installed into a non-default schema and called
2+
-- WITHOUT that schema on the search_path. Every function pins its own
3+
-- search_path to @extschema@, so qualified calls resolve their tables.
4+
\set ECHO none
5+
SELECT pgos_ext.create_user('alice') AS uid
6+
SELECT pgos_ext.create_role('ops') AS rid
7+
SELECT pgos_ext.assign_role_to_user(1, 1);
8+
assign_role_to_user
9+
---------------------
10+
11+
(1 row)
12+
13+
SELECT pgos_ext.grant_permission_to_role(1, 'process', 'execute');
14+
grant_permission_to_role
15+
--------------------------
16+
17+
(1 row)
18+
19+
SELECT pgos_ext.grant_permission_to_role(1, 'memory', 'allocate');
20+
grant_permission_to_role
21+
--------------------------
22+
23+
(1 row)
24+
25+
SELECT pgos_ext.grant_permission_to_role(1, 'file', 'write');
26+
grant_permission_to_role
27+
--------------------------
28+
29+
(1 row)
30+
31+
CALL pgos_ext.create_process('p1', 1, 1);
32+
SELECT name, state FROM pgos_ext.processes ORDER BY name;
33+
name | state
34+
------+-------
35+
p1 | new
36+
(1 row)
37+
38+
INSERT INTO pgos_ext.memory_segments (size) VALUES (4096);
39+
SELECT pgos_ext.allocate_memory(1, 1, 1024);
40+
allocate_memory
41+
-----------------
42+
43+
(1 row)
44+
45+
SELECT allocated, allocated_to FROM pgos_ext.memory_segments ORDER BY id;
46+
allocated | allocated_to
47+
-----------+--------------
48+
t | 1
49+
(1 row)
50+
51+
SELECT pgos_ext.create_file(1, 'f.txt', NULL, FALSE) AS fid
52+
SELECT pgos_ext.write_file(1, 1, 'hi');
53+
write_file
54+
------------
55+
56+
(1 row)
57+
58+
SELECT pgos_ext.read_file(1, 1);
59+
read_file
60+
-----------
61+
hi
62+
(1 row)
63+
64+
SELECT pgos_ext.create_mutex('m');
65+
create_mutex
66+
--------------
67+
68+
(1 row)
69+
70+
SELECT name, locked_by_thread FROM pgos_ext.mutexes;
71+
name | locked_by_thread
72+
------+------------------
73+
m |
74+
(1 row)
75+

0 commit comments

Comments
 (0)